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 TerrainerByViper v1.0.10
plugins/Terrainer.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExampleCallMethodMod.Utils; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Terrainer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.10.0")] [assembly: AssemblyInformationalVersion("1.0.10+e2a8511ea022dec2d2b33283399cadecd56412f4")] [assembly: AssemblyProduct("Terrainer")] [assembly: AssemblyTitle("Terrainer")] [assembly: AssemblyVersion("1.0.10.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 ExampleCallMethodMod { internal static class GameAccess { private static readonly FieldRef<Player, GameObject> GhostField = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost"); private static readonly FieldRef<Player, PieceTable> PiecesField = AccessTools.FieldRefAccess<Player, PieceTable>("m_buildPieces"); private static readonly FieldRef<Player, HashSet<string>> RecipesField = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownRecipes"); private static readonly FieldInfo LocalizationField = AccessTools.Field(typeof(Localization), "m_instance"); private static readonly MethodInfo AddKnownPieceMethod = AccessTools.Method(typeof(Player), "AddKnownPiece", new Type[1] { typeof(Piece) }, (Type[])null); private static readonly MethodInfo UpdatePiecesMethod = AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo AddWordMethod = AccessTools.Method(typeof(Localization), "AddWord", new Type[2] { typeof(string), typeof(string) }, (Type[])null); internal static Localization ExistingLocalization => (Localization)LocalizationField.GetValue(null); internal static GameObject Ghost(Player player) { return GhostField.Invoke(player); } internal static PieceTable Pieces(Player player) { return PiecesField.Invoke(player); } internal static HashSet<string> Recipes(Player player) { return RecipesField.Invoke(player); } internal static void AddWord(this Localization localization, string key, string value) { AddWordMethod.Invoke(localization, new object[2] { key, value }); } internal static void LearnPiece(Player player, Piece piece) { AddKnownPieceMethod.Invoke(player, new object[1] { piece }); UpdatePiecesMethod.Invoke(player, null); } } public class GhostVisualInfo { public string VisualPath; public Vector3 BaseScale; public float BaseRadius; } [BepInPlugin("com.github.terrainer.Terrainer", "Terrainer", "1.0.10")] public class Mod : BaseUnityPlugin { public static readonly Dictionary<string, GhostVisualInfo> GhostVisuals = new Dictionary<string, GhostVisualInfo>(); private static readonly Harmony harmony = new Harmony("com.github.terrainer.Terrainer"); public const string LevelPieceName = "Terrainer_LevelToHeight"; public const string LevelPieceSquareName = "Terrainer_LevelToHeightSquare"; public const string PieceNamePrefix = "Terrainer_LevelToHeight"; public const string PluginGuid = "com.github.terrainer.Terrainer"; public const string PluginName = "Terrainer"; public const string PluginVersion = "1.0.10"; public static ConfigEntry<float> Radius { get; private set; } public static ConfigEntry<float> HeightStep { get; private set; } public static ConfigEntry<float> HeightStepLarge { get; private set; } public static ConfigEntry<float> RadiusStep { get; private set; } public static ConfigEntry<bool> ConsumeRocks { get; private set; } public static ConfigEntry<float> RocksPerCubicMeter { get; private set; } public static float TargetHeight { get; set; } public static bool HasTargetHeight { get; set; } public static float CurrentRadius { get; set; } public static ManualLogSource Log { get; private set; } private void Awake() { Radius = ((BaseUnityPlugin)this).Config.Bind<float>("Leveler", "Radius", 4f, "Начальный радиус выравнивания (меняется Shift+колесо в игре)."); HeightStep = ((BaseUnityPlugin)this).Config.Bind<float>("Leveler", "HeightStep", 0.25f, "Шаг изменения высоты (Q/E)."); HeightStepLarge = ((BaseUnityPlugin)this).Config.Bind<float>("Leveler", "HeightStepLarge", 2f, "Шаг изменения высоты при зажатом Ctrl (Q/E)."); RadiusStep = ((BaseUnityPlugin)this).Config.Bind<float>("Leveler", "RadiusStep", 0.5f, "Шаг изменения радиуса (Shift+колесо)."); ConsumeRocks = ((BaseUnityPlugin)this).Config.Bind<bool>("Leveler", "ConsumeRocks", false, "Требовать камень за подъём terrain (по объёму добавленной земли)."); RocksPerCubicMeter = ((BaseUnityPlugin)this).Config.Bind<float>("Leveler", "RocksPerCubicMeter", 0.15f, "Камня за кубический метр подъёма (при включённом ConsumeRocks)."); CurrentRadius = Radius.Value; Log = ((BaseUnityPlugin)this).Logger; try { harmony.PatchAll(); } catch (Exception arg) { Log.LogError((object)$"PatchAll упал: {arg}"); } } private void OnDestroy() { harmony.UnpatchSelf(); } private void OnGUI() { //IL_0065: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if (HasTargetHeight && !((Object)(object)Player.m_localPlayer == (Object)null)) { string text = (ModLoc.Russian ? $"Высота: {TargetHeight:F2} Радиус: {CurrentRadius:F1}\nQ/E — высота (Ctrl — крупно) · Shift+колесо — радиус · R — сброс" : $"Height: {TargetHeight:F2} Radius: {CurrentRadius:F1}\nQ/E — height (Ctrl — large) · Shift+wheel — radius · R — reset"); GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 15 }; val.normal.textColor = Color.white; GUIStyle val2 = val; GUI.Label(new Rect((float)Screen.width / 2f + 24f, (float)Screen.height / 2f - 16f, 560f, 48f), text, val2); } } } public static class ModLoc { public const string RoundPieceToken = "$terainer_level_round"; public const string SquarePieceToken = "$terainer_level_square"; public const string DescToken = "$terainer_level_desc"; public static bool Russian { get; private set; } public static string RoundPieceName { get { if (!Russian) { return "Level to height"; } return "Выровнять по высоте"; } } public static string SquarePieceName { get { if (!Russian) { return "Level to height (square)"; } return "Выровнять по высоте (квадрат)"; } } private static string TrimToken(string token) { return token.TrimStart('$'); } public static string Register() { Localization existingLocalization = GameAccess.ExistingLocalization; if (existingLocalization == null) { return null; } Russian = existingLocalization.GetSelectedLanguage() == "Russian"; existingLocalization.AddWord(TrimToken("$terainer_level_round"), RoundPieceName); existingLocalization.AddWord(TrimToken("$terainer_level_square"), SquarePieceName); existingLocalization.AddWord(TrimToken("$terainer_level_desc"), Russian ? "Q/E выбирает высоту, ЛКМ выравнивает terrain в области до неё." : "Q/E picks the height, LMB levels the terrain in the area to it."); return existingLocalization.GetSelectedLanguage(); } } [HarmonyPatch(typeof(Localization), "SetStartupLanguage")] public static class LocalizationStartupPatch { private static void Postfix() { ModLoc.Register(); } } [HarmonyPatch(typeof(Localization), "SetLanguageFromLocale")] public static class LocalizationLocalePatch { private static void Postfix() { ModLoc.Register(); } } [HarmonyPatch(typeof(Localization), "SetLanguage")] public static class LocalizationSetLanguagePatch { private static void Postfix() { ModLoc.Register(); } } [HarmonyPatch(typeof(Player), "PlacePiece")] public static class PlaceHeightPatch { private static void Prefix(Player __instance, Piece piece, ref Vector3 pos) { try { if (Mod.HasTargetHeight && !((Object)(object)piece == (Object)null) && ((Object)piece).name.StartsWith("Terrainer_LevelToHeight", StringComparison.Ordinal)) { pos.y = Mod.TargetHeight; } } catch (Exception arg) { Mod.Log.LogError((object)$"PlaceHeightPatch: {arg}"); } } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] public static class GhostHeightPatch { private static void Postfix(Player __instance) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) try { if (PlayerInputPatch.IsActiveMode(__instance) && !((Object)(object)GameAccess.Ghost(__instance) == (Object)null)) { Transform transform = GameAccess.Ghost(__instance).transform; if (!Mod.HasTargetHeight) { Mod.TargetHeight = transform.position.y; Mod.HasTargetHeight = true; } transform.position = new Vector3(transform.position.x, Mod.TargetHeight, transform.position.z); ApplyRadius(GameAccess.Ghost(__instance).GetComponent<TerrainOp>()); Piece selectedPiece = GameAccess.Pieces(__instance).GetSelectedPiece(); ApplyRadius((selectedPiece != null) ? ((Component)selectedPiece).GetComponent<TerrainOp>() : null); ApplyGhostVisualScale(GameAccess.Ghost(__instance)); } } catch (Exception arg) { Mod.Log.LogError((object)$"GhostHeightPatch: {arg}"); } } public static void ApplyRadius(TerrainOp op) { if (!((Object)(object)op == (Object)null)) { op.m_settings.m_levelRadius = Mod.CurrentRadius; op.m_settings.m_paintRadius = Mod.CurrentRadius; } } public static void ApplyGhostVisualScale(GameObject ghost) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ghost == (Object)null) && Mod.GhostVisuals.TryGetValue(((Object)ghost).name, out var value) && value != null && !(value.BaseRadius <= 0.01f)) { Transform val = ghost.transform.Find(value.VisualPath); if (!((Object)(object)val == (Object)null)) { float num = Mod.CurrentRadius / value.BaseRadius; val.localScale = new Vector3(value.BaseScale.x * num, value.BaseScale.y, value.BaseScale.z * num); } } } } [HarmonyPatch(typeof(Player), "Update")] public static class PlayerInputPatch { private const float ScrollThreshold = 0.05f; private const float KeyRepeatInterval = 0.12f; private static float scrollAccumulator; private static float keyRepeatTimer; public static bool IsActiveMode(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).InPlaceMode()) { return false; } GameObject val = GameAccess.Ghost(player); if ((Object)(object)val != (Object)null && ((Object)val).name.StartsWith("Terrainer_LevelToHeight", StringComparison.Ordinal)) { return true; } PieceTable obj = GameAccess.Pieces(player); Piece val2 = ((obj != null) ? obj.GetSelectedPiece() : null); if ((Object)(object)val2 != (Object)null) { return ((Object)val2).name.StartsWith("Terrainer_LevelToHeight", StringComparison.Ordinal); } return false; } private static void Postfix(Player __instance) { //IL_0211: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } EnsurePieceKnown(__instance); if (!Mod.HasTargetHeight && !IsActiveMode(__instance)) { return; } if (!IsActiveMode(__instance) || Hud.IsPieceSelectionVisible()) { scrollAccumulator = 0f; keyRepeatTimer = 0f; if (!IsActiveMode(__instance)) { Mod.HasTargetHeight = false; } return; } bool flag = ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true); float num = (flag ? Mod.HeightStepLarge.Value : Mod.HeightStep.Value); keyRepeatTimer -= Time.deltaTime; bool key = ZInput.GetKey((KeyCode)113, true); bool key2 = ZInput.GetKey((KeyCode)101, true); if ((key || key2) && keyRepeatTimer <= 0f) { keyRepeatTimer = 0.12f; if (key) { Mod.TargetHeight += num; } if (key2) { Mod.TargetHeight -= num; } if ((ZInput.GetKey((KeyCode)304, true) || ZInput.GetKey((KeyCode)303, true)) && num > 0f) { Mod.TargetHeight = Mathf.Round(Mod.TargetHeight / num) * num; } } if (ZInput.GetKey((KeyCode)304, true) || ZInput.GetKey((KeyCode)303, true)) { scrollAccumulator += ZInput.GetMouseScrollWheel(); float num2 = (flag ? (Mod.RadiusStep.Value * 4f) : Mod.RadiusStep.Value); while (scrollAccumulator >= 0.05f) { scrollAccumulator -= 0.05f; Mod.CurrentRadius = Mathf.Clamp(Mod.CurrentRadius + num2, 1f, 16f); } while (scrollAccumulator <= -0.05f) { scrollAccumulator += 0.05f; Mod.CurrentRadius = Mathf.Clamp(Mod.CurrentRadius - num2, 1f, 16f); } } else { scrollAccumulator = 0f; } if (ZInput.GetButtonDown("Rotate")) { Mod.TargetHeight = ZoneSystem.instance.GetGroundHeight(((Component)Player.m_localPlayer).transform.position); } } catch (Exception arg) { Mod.Log.LogError((object)$"PlayerInputPatch: {arg}"); } } private static void EnsurePieceKnown(Player player) { if ((Object)(object)player != (Object)(object)Player.m_localPlayer || GameAccess.Recipes(player) == null || (GameAccess.Recipes(player).Contains("$terainer_level_round") && GameAccess.Recipes(player).Contains("$terainer_level_square"))) { return; } Dictionary<string, string> dictionary = new Dictionary<string, string> { { "Terrainer_LevelToHeight", "$terainer_level_round" }, { "Terrainer_LevelToHeightSquare", "$terainer_level_square" } }; List<PieceTable> list = new List<PieceTable>(); ((Humanoid)player).GetInventory().GetAllPieceTables(list); foreach (PieceTable item in list) { foreach (GameObject piece in item.m_pieces) { if ((Object)(object)piece != (Object)null && dictionary.TryGetValue(((Object)piece).name, out var value) && !GameAccess.Recipes(player).Contains(value)) { GameAccess.LearnPiece(player, piece.GetComponent<Piece>()); } } } } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] public static class RockCostPatch { private static int pendingCost; private static bool Prefix(Player __instance, Piece piece, ref bool __result) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) pendingCost = 0; if ((Object)(object)piece == (Object)null || !((Object)piece).name.StartsWith("Terrainer_LevelToHeight", StringComparison.Ordinal) || !Mod.ConsumeRocks.Value || (Object)(object)GameAccess.Ghost(__instance) == (Object)null) { return true; } try { Vector3 position = GameAccess.Ghost(__instance).transform.position; bool square = ((Object)piece).name == "Terrainer_LevelToHeightSquare"; pendingCost = ComputeStoneCost(position, square); } catch (Exception arg) { Mod.Log.LogError((object)$"RockCostPatch: {arg}"); } if (pendingCost <= 0) { return true; } int num = ((Humanoid)__instance).GetInventory().CountItems("$item_stone", -1, true); if (num < pendingCost) { string text = (ModLoc.Russian ? $"Камень: нужно {pendingCost}, есть {num}" : $"Stone: need {pendingCost}, have {num}"); ((Character)__instance).Message((MessageType)2, text, 0, (Sprite)null, false); __result = false; return false; } return true; } private static void Postfix(Player __instance, bool __result) { if (__result && pendingCost > 0) { ((Humanoid)__instance).GetInventory().RemoveItem("$item_stone", pendingCost, -1, true); } pendingCost = 0; } private static int ComputeStoneCost(Vector3 pos, bool square) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) float currentRadius = Mod.CurrentRadius; float targetHeight = Mod.TargetHeight; float num = 0f; float num2 = currentRadius * currentRadius; for (float num3 = 0f - currentRadius; num3 <= currentRadius + 0.01f; num3 += 0.5f) { for (float num4 = 0f - currentRadius; num4 <= currentRadius + 0.01f; num4 += 0.5f) { if (square || !(num3 * num3 + num4 * num4 > num2)) { float groundHeight = ZoneSystem.instance.GetGroundHeight(pos + new Vector3(num3, 0f, num4)); if (targetHeight > groundHeight) { num += (targetHeight - groundHeight) * 0.25f; } } } } return Mathf.CeilToInt(num * Mod.RocksPerCubicMeter.Value); } } [HarmonyPatch(typeof(TerrainComp), "ApplyOperation")] internal static class TerrainOperationPatch { private static readonly MethodInfo CheckLoad = AccessTools.Method(typeof(TerrainComp), "CheckLoad", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo DoOperation = AccessTools.Method(typeof(TerrainComp), "DoOperation", new Type[3] { typeof(Vector3), typeof(Vector3), typeof(Settings) }, (Type[])null); private static bool Prefix(TerrainComp __instance, TerrainOp modifier) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00c4: 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) if ((Object)(object)modifier == (Object)null) { return true; } string prefabName = Utils.GetPrefabName(((Object)((Component)modifier).gameObject).name); if (prefabName != "Terrainer_LevelToHeight" && prefabName != "Terrainer_LevelToHeightSquare") { return true; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { throw new InvalidOperationException("Terrainer: terrain network view is not ready."); } component.ClaimOwnership(); if (!component.IsOwner()) { throw new InvalidOperationException("Terrainer: could not acquire terrain ownership."); } CheckLoad.Invoke(__instance, null); Vector3 position = ((Component)modifier).transform.position; Vector3 val = (modifier.m_settings.m_rotation ? ((Component)modifier).transform.forward : Vector3.zero); DoOperation.Invoke(__instance, new object[3] { position, val, modifier.m_settings }); Mod.Log.LogInfo((object)$"Applied {prefabName}: height={position.y:F2}, radius={modifier.m_settings.m_levelRadius:F2}"); return false; } } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class ZNetScenePatch { private const int CylinderSegments = 32; private static Mesh cylinderMesh; private static void Postfix(ZNetScene __instance) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hoe"); PieceTable val = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces); if ((Object)(object)val == (Object)null || (Object)(object)__instance == (Object)null) { Mod.Log.LogWarning((object)"Не найдена таблица пьесов мотыги, режимы не добавлены"); return; } int num = val.m_pieces.RemoveAll((GameObject p) => (Object)(object)p == (Object)null); if (num > 0) { Mod.Log.LogInfo((object)$"Убрано {num} уничтоженных пьесов из таблицы мотыги"); } if (val.m_pieces.Any((GameObject p) => ((Object)p).name == "Terrainer_LevelToHeight")) { return; } GameObject val2 = null; foreach (GameObject piece in val.m_pieces) { if ((Object)(object)((piece != null) ? piece.GetComponentInChildren<TerrainOp>() : null) != (Object)null) { val2 = piece; break; } } if ((Object)(object)val2 == (Object)null) { Mod.Log.LogWarning((object)"В таблице мотыги не найдено ни одного пьеса с TerrainOp, режимы не добавлены"); return; } RegisterClone(__instance, val, val2, "Terrainer_LevelToHeight", "$terainer_level_round", square: false); RegisterClone(__instance, val, val2, "Terrainer_LevelToHeightSquare", "$terainer_level_square", square: true); ModLoc.Register(); } private static void RegisterClone(ZNetScene scene, PieceTable table, GameObject source, string cloneName, string nameToken, bool square) { TerrainOp.m_forceDisableTerrainOps = true; GameObject val = Object.Instantiate<GameObject>(source, ((Component)scene).transform); TerrainOp.m_forceDisableTerrainOps = false; ((Object)val).name = cloneName; TerrainOp componentInChildren = val.GetComponentInChildren<TerrainOp>(); componentInChildren.m_settings.m_level = true; componentInChildren.m_settings.m_levelOffset = 0f; componentInChildren.m_settings.m_raise = false; componentInChildren.m_settings.m_smooth = false; componentInChildren.m_settings.m_square = square; componentInChildren.m_settings.m_levelRadius = Mod.Radius.Value; componentInChildren.m_settings.m_paintRadius = Mathf.Max(componentInChildren.m_settings.m_paintRadius, Mod.Radius.Value); Piece component = val.GetComponent<Piece>(); component.m_name = nameToken; component.m_description = "$terainer_level_desc"; component.m_canRotate = true; GhostVisualInfo ghostVisualInfo = SetupGhostVisual(val, square); if (ghostVisualInfo != null) { Mod.GhostVisuals[cloneName] = ghostVisualInfo; } if (!scene.m_prefabs.Contains(val)) { scene.m_prefabs.Add(val); } if (!table.m_pieces.Contains(val)) { table.m_pieces.Add(val); } Mod.Log.LogInfo((object)($"Режим '{nameToken}' добавлен в таблицу мотыги (на основе {((Object)source).name}, радиус {Mod.Radius.Value}" + ((ghostVisualInfo != null) ? $", визуал {ghostVisualInfo.BaseRadius:F2} м" : ", визуал не найден") + ")")); } private static GhostVisualInfo SetupGhostVisual(GameObject clone, bool square) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_008a: 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_00aa: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) MeshFilter componentInChildren = clone.GetComponentInChildren<MeshFilter>(true); if ((Object)(object)componentInChildren == (Object)null) { return null; } if (square) { Transform transform = ((Component)componentInChildren).transform; return new GhostVisualInfo { VisualPath = GetRelativePath(clone.transform, transform), BaseScale = transform.localScale, BaseRadius = MeasureSubtreeRadius(transform) }; } MeshRenderer component = ((Component)componentInChildren).GetComponent<MeshRenderer>(); GameObject val = new GameObject("TerrainerVisual"); val.transform.SetParent(clone.transform, false); val.transform.localPosition = new Vector3(0f, -0.5f, 0f); val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; val.AddComponent<MeshFilter>().sharedMesh = GetCylinderMesh(); MeshRenderer val2 = val.AddComponent<MeshRenderer>(); if ((Object)(object)component != (Object)null) { ((Renderer)val2).sharedMaterials = ((Renderer)component).sharedMaterials; } if ((Object)(object)((Component)componentInChildren).transform != (Object)(object)clone.transform) { ((Component)componentInChildren).gameObject.SetActive(false); } return new GhostVisualInfo { VisualPath = ((Object)val).name, BaseScale = val.transform.localScale, BaseRadius = 1f }; } private static float MeasureSubtreeRadius(Transform root) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) //IL_003c: 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_0056: 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) //IL_0072: Unknown result type (might be due to invalid IL or missing references) float num = 0f; MeshFilter[] componentsInChildren = ((Component)root).GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { Mesh sharedMesh = val.sharedMesh; if (!((Object)(object)sharedMesh == (Object)null)) { Bounds bounds = sharedMesh.bounds; Vector3 lossyScale = ((Component)val).transform.lossyScale; num = Mathf.Max(new float[3] { num, ((Bounds)(ref bounds)).extents.x * Mathf.Abs(lossyScale.x), ((Bounds)(ref bounds)).extents.z * Mathf.Abs(lossyScale.z) }); } } return num; } private static string GetRelativePath(Transform root, Transform node) { string text = ((Object)node).name; while ((Object)(object)node.parent != (Object)null && (Object)(object)node.parent != (Object)(object)root) { node = node.parent; text = ((Object)node).name + "/" + text; } return text; } private static Mesh GetCylinderMesh() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Expected O, but got Unknown if ((Object)(object)cylinderMesh != (Object)null) { return cylinderMesh; } List<Vector3> list = new List<Vector3>(); List<Vector3> list2 = new List<Vector3>(); List<int> list3 = new List<int>(); int count = list.Count; list.Add(new Vector3(0f, 0.5f, 0f)); list2.Add(Vector3.up); int count2 = list.Count; Vector3 val = default(Vector3); for (int i = 0; i <= 32; i++) { float num = (float)i * MathF.PI * 2f / 32f; ((Vector3)(ref val))..ctor(Mathf.Cos(num), 0f, Mathf.Sin(num)); list.Add(val + new Vector3(0f, 0.5f, 0f)); list2.Add(Vector3.up); } int count3 = list.Count; Vector3 val2 = default(Vector3); for (int j = 0; j <= 32; j++) { float num2 = (float)j * MathF.PI * 2f / 32f; ((Vector3)(ref val2))..ctor(Mathf.Cos(num2), 0f, Mathf.Sin(num2)); list.Add(val2 + new Vector3(0f, 0.5f, 0f)); list2.Add(val2); } int count4 = list.Count; Vector3 val3 = default(Vector3); for (int k = 0; k <= 32; k++) { float num3 = (float)k * MathF.PI * 2f / 32f; ((Vector3)(ref val3))..ctor(Mathf.Cos(num3), 0f, Mathf.Sin(num3)); list.Add(val3 - new Vector3(0f, 0.5f, 0f)); list2.Add(val3); } for (int l = 0; l < 32; l++) { int item = count; int item2 = count2 + l; int item3 = count2 + l + 1; list3.Add(item); list3.Add(item3); list3.Add(item2); list3.Add(item); list3.Add(item2); list3.Add(item3); int item4 = count3 + l; int item5 = count3 + l + 1; int item6 = count4 + l; int item7 = count4 + l + 1; list3.Add(item4); list3.Add(item6); list3.Add(item7); list3.Add(item4); list3.Add(item7); list3.Add(item5); list3.Add(item4); list3.Add(item7); list3.Add(item6); list3.Add(item4); list3.Add(item5); list3.Add(item7); } cylinderMesh = new Mesh { vertices = list.ToArray(), normals = list2.ToArray(), triangles = list3.ToArray() }; cylinderMesh.RecalculateBounds(); ((Object)cylinderMesh).name = "TerrainerCylinder"; return cylinderMesh; } } } namespace ExampleCallMethodMod.Utils { public static class ArrayUtils { public const string NullString = "<null>"; public static IEnumerable<object> ToArray(object obj) { if (!(obj is IEnumerable source)) { if (obj == null) { return Enumerable.Empty<object>(); } return new object[1] { obj }; } return source.Cast<object>(); } public static IEnumerable<string> ToStringArray(object obj) { return from o in ToArray(obj) select o?.ToString() ?? "<null>"; } } public static class CommandParser { public enum SegmentType { Unknown, Int, Float, String, Identifier } public class CommandSegment { public SegmentType Type { get; } public string Text { get; } public CommandSegment(SegmentType type, string text) { Type = type; Text = text ?? ""; } public override string ToString() { return Text; } } public class TakerResult { public CommandSegment Segment { get; } public int Offset { get; } public TakerResult(CommandSegment segment) { Segment = segment; Offset = segment?.Text.Length ?? 0; } public TakerResult(CommandSegment segment, int offset) : this(segment) { Segment = segment; Offset = offset; } } private static readonly Func<string, int, TakerResult>[] segmentTakers = new Func<string, int, TakerResult>[4] { TakeInt, TakeFloat, TakeString, TakeIdentifier }; public static IEnumerable<CommandSegment> Parse(string input) { if (string.IsNullOrWhiteSpace(input)) { yield break; } int offset = 0; while (true) { TakerResult takerResult2; TakerResult takerResult = (takerResult2 = TakeSegment(input, offset)); if (takerResult2 != null) { yield return takerResult.Segment; offset = takerResult.Offset; continue; } break; } } private static TakerResult TakeSegment(string input, int offset, SegmentType type = SegmentType.Unknown) { while (true) { if (offset >= input.Length) { return null; } if (input[offset] != ' ') { break; } offset++; } int startIndex = offset; TakerResult takerResult = null; Func<string, int, TakerResult>[] array = segmentTakers; for (int i = 0; i < array.Length; i++) { takerResult = array[i](input, offset); if (takerResult?.Segment != null) { break; } } return takerResult ?? new TakerResult(new CommandSegment(SegmentType.Unknown, input.Substring(startIndex))); } private static TakerResult TakeIdentifier(string input, int offset) { Match match = Regex.Match(input.Substring(offset), "^\\w+"); CommandSegment obj = (match.Success ? new CommandSegment(SegmentType.Identifier, match.Value) : null); return new TakerResult(obj, (offset + obj?.Text.Length) ?? offset); } private static TakerResult TakeInt(string input, int offset) { Match match = Regex.Match(input.Substring(offset), "^\\-?\\d+"); CommandSegment obj = (match.Success ? new CommandSegment(SegmentType.Int, match.Value) : null); return new TakerResult(obj, (offset + obj?.Text.Length) ?? offset); } private static TakerResult TakeFloat(string input, int offset) { Match match = Regex.Match(input.Substring(offset), "^\\-?\\d+(?:\\.\\d+)?"); CommandSegment obj = (match.Success ? new CommandSegment(SegmentType.Float, match.Value) : null); return new TakerResult(obj, (offset + obj?.Text.Length) ?? offset); } private static TakerResult TakeString(string input, int offset) { if (input[offset] != '"') { return null; } StringBuilder stringBuilder = new StringBuilder(); offset++; while (offset < input.Length) { if (input[offset] == '\\' && offset + 1 < input.Length) { stringBuilder.Append(input[offset + 1]); offset += 2; continue; } if (input[offset] == '"') { offset++; break; } stringBuilder.Append(input[offset]); offset++; } return new TakerResult(new CommandSegment(SegmentType.String, stringBuilder.ToString()), offset); } } } namespace ExampleCallMethodMod.Patches { public static class ConsolePatch { [HarmonyPatch(typeof(Console), "IsConsoleEnabled")] public static class ConsoleEnabled { public static bool Prefix(ref bool __result) { __result = true; return false; } } [HarmonyPatch(typeof(Terminal), "InputText")] public static class InputText { public static bool Prefix() { Console instance = Console.instance; string text = ((TMP_InputField)((Terminal)instance).m_input).text; if (!text.StartsWith("call", ignoreCase: true, CultureInfo.InvariantCulture)) { return true; } CommandParser.CommandSegment[] parts = CommandParser.Parse(text).ToArray(); if (parts.Length < 3) { ((Terminal)instance).AddString("call command requires at least 3 arguments: call <class> <methodName> [arg1] [arg2]"); return false; } ((Terminal)instance).AddString(text); Type type = typeof(Console).Assembly.GetTypes().FirstOrDefault((Type t) => t.Name.Equals(parts[1].Text, StringComparison.InvariantCultureIgnoreCase)); if (type == null) { ((Terminal)instance).AddString($"Could not find class with name '{parts[1]}'"); return false; } MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod).FirstOrDefault((MethodInfo m) => m.Name.Equals(parts[2].Text, StringComparison.InvariantCultureIgnoreCase)); if (methodInfo == null) { ((Terminal)instance).AddString("Could not find method '" + parts[2].Text + "' on class '" + type.FullName + "'"); return false; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length != parts.Length - 3) { ((Terminal)instance).AddString("Command does not match method parameters. Expected parameter types: " + string.Join(", ", parameters.Select((ParameterInfo p) => p.ParameterType.Name))); return false; } object[] array = new object[parameters.Length]; for (int num = 0; num < parameters.Length; num++) { string text2 = parts[num + 3].Text; Type parameterType = parameters[num].ParameterType; try { array[num] = TypeDescriptor.GetConverter(parameterType).ConvertFrom(text2); } catch (NotSupportedException) { ((Terminal)instance).AddString($"Cannot convert argument {num + 1} '{text2}' to type of '{parameterType.FullName}'"); return false; } } object obj = null; if (!methodInfo.IsStatic) { obj = ((type == typeof(Player)) ? Player.m_localPlayer : type.GetField("m_instance", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null)); if (obj == null) { ((Terminal)instance).AddString("Could not call method '" + methodInfo.Name + "' because it's an instance method and no static instance field is defined on it"); return false; } } object obj2 = methodInfo.Invoke(obj, array); if (obj2 != null) { ((Terminal)instance).AddString(string.Join(", ", ArrayUtils.ToStringArray(obj2))); } return false; } } } }