using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using Zen; using Zen.Lib; using Zen.Lib.Config; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ZenPath")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ZenPath")] [assembly: AssemblyCopyright("Copyright \ufffd 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 ZenPath { internal static class Configs { public static readonly ConfigEntry StaminaDrainPath; public static readonly ConfigEntry StaminaDrainPaved; public static readonly ConfigEntry MoveSpeedSnow; public static readonly ConfigEntry GroundIsSnowKeys; public static readonly ConfigEntry ShowStatusIcon; static Configs() { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00af: Expected O, but got Unknown StaminaDrainPath = Config.Define(true, "General", "Stamina Usage Path", 0.5f, Config.AcceptRange(0f, 1f), "Stamina drain percent when running on dirt, wood, or metal (Vanilla: 1)"); StaminaDrainPaved = Config.Define(true, "General", "Stamina Usage Paved", 0f, Config.AcceptRange(0f, 1f), "Stamina drain percent when running on a paved surface or stone. (Vanilla: 1)"); MoveSpeedSnow = Config.Define(true, "General", "Move Speed Snow", 0.75f, Config.AcceptRange(0.1f, 1f), "Movement speed when on snow as a percentage. (Vanilla: 1)"); StringList val = new StringList(); ((List)val).Add("season_winter"); GroundIsSnowKeys = Config.Define(true, "General", "Ground Is Snow", val, "The ground is treated as snow if any of these keys are present.\r\nComma separated list of global keys or per-player keys.\r\n[Seasons does not enable the global keys by default. Turn them on in the Seasons configs]"); ShowStatusIcon = Config.Define(true, "General", "Show Status Icon", true, "Display the status effect icon when running on paths"); } } internal static class HeightmapExt { public static Color GetPaintMask(this Heightmap heightmap, Vector3 worldPos) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) worldPos.x -= 0.5f; worldPos.z -= 0.5f; int num = default(int); int num2 = default(int); heightmap.WorldToVertex(worldPos, ref num, ref num2); return heightmap.GetPaintMask(num, num2); } public static bool IsPaintMaskOf(this Color color, Color mask) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (color.r >= mask.r * 0.5f && color.g >= mask.g * 0.5f) { return color.b >= mask.b * 0.5f; } return false; } public static bool IsDirt(this Heightmap heightmap, Vector3 worldPos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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) return heightmap.GetPaintMask(worldPos).IsPaintMaskOf(Heightmap.m_paintMaskDirt); } public static bool IsPaved(this Heightmap heightmap, Vector3 worldPos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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) return heightmap.GetPaintMask(worldPos).IsPaintMaskOf(Heightmap.m_paintMaskPaved); } } [HarmonyPatch] internal static class PathDetect { private static MotionType _motionType; public static GroundMaterial GroundMaterial { get; private set; } public static PathType PathType { get; private set; } public static bool IsRunning { get { //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) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 MotionType motionType = _motionType; if ((int)motionType == 2 || (int)motionType == 8) { return true; } return false; } } public static bool IsOnPath => PathType != PathType.None; public static bool IsOnSnow { get { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if (PathType == PathType.None) { return (int)GroundMaterial == 16; } return false; } } public static bool IsRunningOnPath { get { if (IsOnPath) { return IsRunning; } return false; } } public static bool IsRunningOnSnow { get { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 if (IsOnSnow) { if (!IsRunning) { return (int)_motionType == 1; } return true; } return false; } } private static PathType GetPathType(Collider? collider, Vector3 worldPos) { //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) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected I4, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Invalid comparison between Unknown and I4 //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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) GroundMaterial groundMaterial = GroundMaterial; if ((int)groundMaterial <= 64) { if ((int)groundMaterial <= 16) { switch ((int)groundMaterial) { default: if ((int)groundMaterial == 16) { break; } goto IL_0085; case 0: case 2: break; case 8: goto IL_0081; case 4: return PathType.PavedPath; case 1: case 3: case 5: case 6: case 7: goto IL_0085; } goto IL_007f; } if ((int)groundMaterial != 32 && (int)groundMaterial == 64) { } } else if ((int)groundMaterial <= 256) { if ((int)groundMaterial != 128 && (int)groundMaterial == 256) { goto IL_0081; } } else if ((int)groundMaterial == 512 || ((int)groundMaterial != 1024 && (int)groundMaterial == 2048)) { goto IL_007f; } goto IL_0085; IL_007f: return PathType.None; IL_0081: return PathType.SimplePath; IL_0085: if (!Object.op_Implicit((Object)(object)collider)) { return PathType.None; } Heightmap component = ((Component)collider).GetComponent(); if (!Object.op_Implicit((Object)(object)component)) { return PathType.None; } Color paintMask = component.GetPaintMask(worldPos); if (paintMask.IsPaintMaskOf(Heightmap.m_paintMaskDirt)) { return PathType.SimplePath; } if (paintMask.IsPaintMaskOf(Heightmap.m_paintMaskPaved)) { return PathType.PavedPath; } if (IsGroundAlwaysSnow(component, worldPos)) { GroundMaterial = (GroundMaterial)16; } return PathType.None; } private static bool IsGroundAlwaysSnow(Heightmap heightmap, Vector3 worldPos) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0039: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 if (((IEnumerable)Configs.GroundIsSnowKeys.Value).Any((string k) => ZoneSystem.instance.GetGlobalKey(k) || ((Humanoid)Player.m_localPlayer).HaveUniqueKey(k))) { GroundMaterial groundMaterial = GroundMaterial; if ((int)groundMaterial != 32) { if ((int)groundMaterial == 64 || (int)groundMaterial == 128) { goto IL_0053; } } else if (heightmap.HaveBiome((Biome)2)) { goto IL_0053; } } return false; IL_0053: return !heightmap.IsCleared(worldPos); } [HarmonyPostfix] [HarmonyPatch(typeof(FootStep), "OnFoot", new Type[] { typeof(Transform) })] private static void FootStep_OnFoot(FootStep __instance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (PlayerExt.Is(Player.m_localPlayer, __instance.m_character)) { Logging.Info((object)$"GroundType: {GroundMaterial}, MotionType: {_motionType}, PathType: {PathType}", 0); Effect.UpdateStep(); } } [HarmonyPostfix] [HarmonyPatch(typeof(FootStep), "GetMotionType")] private static void FootStep_GetMotionType(Character character, ref MotionType __result) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (PlayerExt.Is(Player.m_localPlayer, character)) { _motionType = __result; } } [HarmonyPostfix] [HarmonyPatch(typeof(FootStep), "GetGroundMaterial")] private static void FootStep_GetGroundMaterial(Character character, Vector3 point, GroundMaterial __result) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (PlayerExt.Is(Player.m_localPlayer, character)) { GroundMaterial = __result; PathType = GetPathType(character.GetLastGroundCollider(), point); } } [HarmonyTranspiler] [HarmonyPatch(typeof(FootStep), "GetGroundMaterial")] private static IEnumerable Transpile_FootStep_GetGroundMaterial_Ashlands(IEnumerable instructions) { FieldInfo fieldMaterialType = AccessTools.Field(typeof(WearNTear), "m_materialType"); Func intercept = GetGroundMaterialIntercept; return Transpilers.Manipulator(instructions, (Func)((CodeInstruction code) => CodeInstructionExtensions.Is(code, OpCodes.Ldfld, (MemberInfo)fieldMaterialType)), (Action)delegate(CodeInstruction code) { code.opcode = OpCodes.Call; code.operand = intercept.Method; }); static MaterialType GetGroundMaterialIntercept(WearNTear wnt) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_0029: Expected I4, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) MaterialType materialType = wnt.m_materialType; switch ((int)materialType) { case 0: case 3: return (MaterialType)0; case 2: return (MaterialType)2; case 1: case 4: case 5: case 6: return (MaterialType)1; default: Logging.Info((object)$"WearNTear.MaterialType: {wnt.m_materialType} is out of range. Did a Valheim update add a new type? Is it a mod?", 0); return wnt.m_materialType; } } } } internal enum PathType { None, SimplePath, PavedPath } [BepInPlugin("ZenDragon.ZenPath", "ZenPath", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class Plugin : ZenMod { public const string PluginName = "ZenPath"; public const string PluginVersion = "1.0.0"; public const string PluginGUID = "ZenDragon.ZenPath"; protected override void Setup() { } protected override void TitleScene(bool isFirstBoot) { } protected override void WorldStart() { Effect.Init(); } private void Update() { Effect.Update(); } protected override void Shutdown() { Effect.Shutdown(); } protected override HelpInfo? GetHelpInfo() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(HelpInfo.Format(Configs.StaminaDrainPath, true, true, true)); stringBuilder.AppendLine(HelpInfo.Format(Configs.StaminaDrainPaved, true, true, true)); return new HelpInfo("Path", stringBuilder.ToString()); } } internal static class Effect { private class BaseEffect : StatusEffect { private const float Cooldown = 1f; public float initTime; public bool IsExpired => Time.time >= initTime + 1f; public BaseEffect() { base.m_icon = (Configs.ShowStatusIcon.Value ? ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("CorpseRun")).m_icon : null); } } private class SnowEffect : BaseEffect { public SnowEffect() { ((Object)this).name = "SE_ZenPath_Snow"; ((StatusEffect)this).m_name = "$se_path_snow_name"; ((StatusEffect)this).m_tooltip = "$se_path_snow_tooltip"; } public override void ModifySpeed(float baseSpeed, ref float speed, Character character, Vector3 dir) { if (PlayerExt.Is(Player.m_localPlayer, character) && PathDetect.IsOnSnow) { speed = baseSpeed * Configs.MoveSpeedSnow.Value; Logging.Info((object)$"Snow Speed: {speed:F2}", 0); } } } private class PathEffect : BaseEffect { private PathType _pathType; public PathEffect() { ((Object)this).name = "SE_ZenPath"; } public override void ModifyRunStaminaDrain(float baseDrain, ref float drain, Vector3 dir) { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if (PathDetect.IsOnPath) { _pathType = PathDetect.PathType; float value; switch (_pathType) { case PathType.SimplePath: value = Configs.StaminaDrainPath.Value; ((StatusEffect)this).m_name = "$se_path_dirt_name"; ((StatusEffect)this).m_tooltip = "$se_path_dirt_tooltip"; break; case PathType.PavedPath: value = Configs.StaminaDrainPaved.Value; ((StatusEffect)this).m_name = "$se_path_paved_name"; ((StatusEffect)this).m_tooltip = "$se_path_paved_tooltip"; break; case PathType.None: Logging.Info((object)"No path detected, not applying stamina drain", 0); return; default: throw new ArgumentOutOfRangeException("_pathType"); } drain *= Mathf.Max(value, 0.0001f); if (PlayerExt.IsReady(Player.m_localPlayer) && PathDetect.IsRunning) { Logging.Info((object)$"Stamina Drain: {1f / (baseDrain / drain):P0}\tPathType: {_pathType} : {PathDetect.GroundMaterial}", 0); } } } } private static PathEffect _pathEffect; private static SnowEffect _snowEffect; private static bool _initialized; public static void Init() { _pathEffect = ScriptableObject.CreateInstance(); _snowEffect = ScriptableObject.CreateInstance(); ObjectDB.instance.m_StatusEffects.Add((StatusEffect)(object)_pathEffect); ObjectDB.instance.m_StatusEffects.Add((StatusEffect)(object)_snowEffect); _initialized = true; } public static void Shutdown() { if (Object.op_Implicit((Object)(object)ObjectDB.instance)) { ObjectDB.instance.m_StatusEffects.Remove((StatusEffect)(object)_pathEffect); ObjectDB.instance.m_StatusEffects.Remove((StatusEffect)(object)_snowEffect); Object.Destroy((Object)(object)_pathEffect); Object.Destroy((Object)(object)_snowEffect); } } public static void UpdateStep() { if (PathDetect.IsRunningOnPath) { RemoveEffect(_snowEffect); ApplyEffect(_pathEffect); } else if (PathDetect.IsRunningOnSnow && Configs.MoveSpeedSnow.Value < 1f) { RemoveEffect(_pathEffect); ApplyEffect(_snowEffect); } } private static void ApplyEffect(BaseEffect effect) { if (PlayerExt.IsReady(Player.m_localPlayer)) { if (effect.IsExpired) { ((Character)Player.m_localPlayer).GetSEMan().AddStatusEffect((StatusEffect)(object)effect, false, 0, 0f); } effect.initTime = Time.time; } } private static void RemoveEffect(BaseEffect effect) { if (PlayerExt.IsReady(Player.m_localPlayer)) { ((Character)Player.m_localPlayer).GetSEMan().RemoveStatusEffect((StatusEffect)(object)effect, false); effect.initTime = 0f; } } public static void Update() { if (_initialized) { if (_pathEffect.IsExpired && !PathDetect.IsRunningOnPath) { RemoveEffect(_pathEffect); } if (_snowEffect.IsExpired && !PathDetect.IsRunningOnSnow) { RemoveEffect(_snowEffect); } } } } }