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.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")] [assembly: AssemblyCompany("TrueLevel")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.3.0")] [assembly: AssemblyInformationalVersion("0.1.3")] [assembly: AssemblyProduct("TrueLevel")] [assembly: AssemblyTitle("TrueLevel")] [assembly: AssemblyVersion("0.1.3.0")] namespace TrueLevel; [BepInPlugin("local.valheim.truelevel", "True Level", "0.1.3")] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Terminal), "TryRunCommand", new Type[] { typeof(string), typeof(bool), typeof(bool) })] private static class CommandPatch { private static bool Prefix(Terminal __instance, string __0) { string[] array = (__0 ?? "").Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0 || !array[0].Equals("truelevel", StringComparison.OrdinalIgnoreCase)) { return true; } if (!Object.op_Implicit((Object)(object)Instance) || !((Behaviour)Instance).enabled) { __instance.AddString("True Level is unavailable."); return false; } if (array.Length > 2) { __instance.AddString("Usage: /truelevel [on|off]"); return false; } string text = ((array.Length == 1) ? "" : array[1].ToLowerInvariant()); switch (text) { default: if (text.Length != 0) { goto case null; } Instance.SetMode(!Instance.enabledMode.Value); break; case "on": Instance.SetMode(value: true); break; case "off": Instance.SetMode(value: false); break; case null: __instance.AddString("Usage: /truelevel [on|off]"); return false; } __instance.AddString("True Level: " + (Instance.enabledMode.Value ? "ON" : "OFF")); return false; } } [HarmonyPatch(typeof(Player), "PlacePiece")] private static class PlacementPatch { private static void Prefix(Player __instance, Piece __0, out float? __state) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) __state = PlacementHeight; PlacementHeight = null; if (Object.op_Implicit((Object)(object)Instance) && ((Behaviour)Instance).enabled && Instance.enabledMode.Value && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && Object.op_Implicit((Object)(object)__0) && IsLevelGround(((Object)((Component)__0).gameObject).name)) { PlacementHeight = ((Component)__instance).transform.position.y; } } private static Exception Finalizer(Exception __exception, float? __state) { PlacementHeight = __state; return __exception; } } [HarmonyPatch(typeof(TerrainComp), "ApplyOperation")] private static class OperationPatch { private static bool Prefix(TerrainComp __instance, TerrainOp __0, ZNetView ___m_nview) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) if (!PlacementHeight.HasValue || !IsLevelGround(((Object)((Component)__0).gameObject).name)) { return true; } if (!Object.op_Implicit((Object)(object)___m_nview) || !___m_nview.IsValid()) { return false; } ___m_nview.ClaimOwnership(); if (!___m_nview.IsOwner()) { ((Character)Player.m_localPlayer).Message((MessageType)2, "True Level: terrain unavailable; try again.", 0, (Sprite)null, false); return false; } CheckLoad.Invoke(__instance, null); Settings val = new Settings(); FieldInfo[] fields = typeof(Settings).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val, fieldInfo.GetValue(__0.m_settings)); } val.m_level = true; val.m_raise = false; val.m_smooth = false; val.m_levelOffset = 0f; Vector3 position = ((Component)__0).transform.position; position.y = PlacementHeight.Value; bool applying = Applying; try { Applying = true; DoOperation.Invoke(__instance, new object[3] { position, ((Component)__0).transform.forward, val }); ((BaseUnityPlugin)Instance).Logger.LogDebug((object)("True Level applied at feet height " + position.y)); } finally { Applying = applying; } return false; } } [HarmonyPatch(typeof(TerrainComp), "LevelTerrain")] private static class LevelPatch { private static bool Prefix(TerrainComp __instance, Vector3 __0, float __1, bool __2, Heightmap ___m_hmap, int ___m_width, float[] ___m_levelDelta, float[] ___m_smoothDelta, bool[] ___m_modifiedHeight) { //IL_000b: 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_0038: Unknown result type (might be due to invalid IL or missing references) if (!Applying) { return true; } int num = default(int); int num2 = default(int); ___m_hmap.WorldToVertex(__0, ref num, ref num2); float num3 = __1 / ___m_hmap.m_scale; int num4 = Mathf.CeilToInt(num3); int num5 = ___m_width + 1; float num6 = __0.y - ((Component)__instance).transform.position.y; for (int i = Math.Max(0, num2 - num4); i <= Math.Min(___m_width, num2 + num4); i++) { for (int j = Math.Max(0, num - num4); j <= Math.Min(___m_width, num + num4); j++) { if (__2 || !((float)((j - num) * (j - num) + (i - num2) * (i - num2)) > num3 * num3)) { int num7 = i * num5 + j; ___m_levelDelta[num7] += num6 - ___m_hmap.GetHeight(j, i) + ___m_smoothDelta[num7]; ___m_smoothDelta[num7] = 0f; ___m_modifiedHeight[num7] = true; } } } return false; } } [HarmonyPatch(typeof(TerrainComp), "ApplyToHeightmap")] private static class DisplayPatch { private static IEnumerable Transpiler(IEnumerable instructions) { List list = instructions.ToList(); MethodInfo methodInfo = AccessTools.Method(typeof(Mathf), "Clamp", new Type[3] { typeof(float), typeof(float), typeof(float) }, (Type[])null); int num = 0; foreach (CodeInstruction item in list) { if (CodeInstructionExtensions.Calls(item, methodInfo)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(DisplayPatch), "Unclamped", (Type[])null, (Type[])null); num++; } } if (num != 1) { throw new InvalidOperationException("Terrain display layout changed; expected exactly one Clamp, found " + num); } return list; } private static float Unclamped(float value, float min, float max) { return value; } } public const string Id = "local.valheim.truelevel"; internal static Plugin Instance; private ConfigEntry toggle; private ConfigEntry enabledMode; private Harmony harmony; internal static float? PlacementHeight; internal static bool Applying; internal static readonly MethodInfo DoOperation = AccessTools.Method(typeof(TerrainComp), "DoOperation", (Type[])null, (Type[])null); internal static readonly MethodInfo CheckLoad = AccessTools.Method(typeof(TerrainComp), "CheckLoad", (Type[])null, (Type[])null); private void Awake() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_0081: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) Instance = this; enabledMode = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", false, "True Level mode. F9 or /truelevel toggles this and saves the setting."); toggle = ((BaseUnityPlugin)this).Config.Bind("General", "ToggleKey", new KeyboardShortcut((KeyCode)290, Array.Empty()), "Toggle True Level while playing."); ConfigEntry val = ((BaseUnityPlugin)this).Config.Bind("General", "KeyMigrationVersion", 0, "Internal setting for the one-time F8 to F9 upgrade."); if (val.Value < 1) { KeyboardShortcut value = toggle.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 289) { value = toggle.Value; if (!((KeyboardShortcut)(ref value)).Modifiers.Any()) { toggle.Value = new KeyboardShortcut((KeyCode)290, Array.Empty()); } } val.Value = 1; } harmony = new Harmony("local.valheim.truelevel"); try { harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"True Level 0.1.3 loaded. F9 or /truelevel toggles hoe leveling."); } catch (Exception ex) { harmony.UnpatchSelf(); ((Behaviour)this).enabled = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Unsupported game or patch conflict; True Level disabled. " + ex)); } } private void Update() { //IL_0040: 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) if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible()) { KeyboardShortcut value = toggle.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { SetMode(!enabledMode.Value); } } } private void SetMode(bool value) { enabledMode.Value = value; if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, "True Level: " + (value ? "ON" : "OFF"), 0, (Sprite)null, false); } } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Instance = null; } internal static bool IsLevelGround(string name) { if (name == null) { return false; } if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { name = name.Substring(0, name.Length - "(Clone)".Length); } if (!(name == "mud_road")) { return name == "mud_road_v2"; } return true; } }