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 TerrainerFixed v1.1.0
BepInEx/plugins/TerrainerFixed.dll
Decompiled a week 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.Security; using System.Security.Permissions; 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: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: IgnoresAccessChecksTo("gui_framework")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Terrainer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.9.0")] [assembly: AssemblyInformationalVersion("1.0.9")] [assembly: AssemblyProduct("Terrainer")] [assembly: AssemblyTitle("Terrainer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.9.0")] [module: UnverifiableCode] [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 { [BepInPlugin("com.github.terrainer.Terrainer", "Terrainer", "1.0.9")] public class Mod : BaseUnityPlugin { 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 static readonly Dictionary<string, GhostVisualInfo> GhostVisuals = new Dictionary<string, GhostVisualInfo>(); public const string PluginGuid = "com.github.terrainer.Terrainer"; public const string PluginName = "Terrainer"; public const string PluginVersion = "1.0.9"; public const string PluginAuthor = "Grib04eg"; 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 ConfigEntry<bool> DebugLogging { 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)."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Leveler", "DebugLogging", false, "Подробный лог (диагностика ghost/режима)."); CurrentRadius = Radius.Value; Log = ((BaseUnityPlugin)this).Logger; try { harmony.PatchAll(); Log.LogInfo((object)$"Патчи применены: {harmony.GetPatchedMethods().Count()} методов"); } 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 — кратно шагу) · Shift+колесо — радиус · R — сброс" : $"Height: {TargetHeight:F2} Radius: {CurrentRadius:F1}\nQ/E — height (Ctrl — large, Shift — snap) · 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 class GhostVisualInfo { public string VisualPath; public Vector3 BaseScale; public float BaseRadius; } 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(new char[1] { '$' }); } public static string Register() { Localization instance = Localization.m_instance; if (instance == null) { return null; } Russian = instance.GetSelectedLanguage() == "Russian"; instance.AddWord(TrimToken("$terainer_level_round"), RoundPieceName); instance.AddWord(TrimToken("$terainer_level_square"), SquarePieceName); instance.AddWord(TrimToken("$terainer_level_desc"), Russian ? "Q/E выбирает высоту, ЛКМ выравнивает terrain в области до неё." : "Q/E picks the height, LMB levels the terrain in the area to it."); return instance.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), "UpdatePlacementGhost")] public static class GhostHeightPatch { private static float lastDiagLog; private static void Postfix(Player __instance) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) try { if (PlayerInputPatch.IsActiveMode(__instance)) { GameObject placementGhost = __instance.m_placementGhost; if (Mod.DebugLogging.Value && Time.time - lastDiagLog > 3f) { lastDiagLog = Time.time; Mod.Log.LogInfo((object)$"GH patch: ghost={((Object)placementGhost).name} y={placementGhost.transform.position.y:F2} hasTarget={Mod.HasTargetHeight} target={Mod.TargetHeight:F2}"); } if (!Mod.HasTargetHeight) { Mod.TargetHeight = placementGhost.transform.position.y; Mod.HasTargetHeight = true; } placementGhost.transform.position = new Vector3(placementGhost.transform.position.x, Mod.TargetHeight, placementGhost.transform.position.z); ApplyRadius(TerrainOpRegistry.Find(placementGhost)); Piece selectedPiece = __instance.m_buildPieces.GetSelectedPiece(); ApplyRadius(TerrainOpRegistry.Find((selectedPiece != null) ? ((Component)selectedPiece).gameObject : null)); ApplyGhostVisualScale(placementGhost); } } 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 = Mathf.Max(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; private static float lastDiagnostic; 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; } if ((Object)(object)player.m_placementGhost != (Object)null && ((Object)player.m_placementGhost).name.StartsWith("Terrainer_LevelToHeight", StringComparison.Ordinal)) { return true; } Piece val = (((Object)(object)player.m_buildPieces != (Object)null) ? player.m_buildPieces.GetSelectedPiece() : null); if ((Object)(object)val != (Object)null) { return ((Object)val).name.StartsWith("Terrainer_LevelToHeight", StringComparison.Ordinal); } return false; } private static void LogBuildState(Player player) { if (Mod.DebugLogging.Value && !(Time.time - lastDiagnostic < 10f)) { lastDiagnostic = Time.time; Piece val = (((Object)(object)player.m_buildPieces != (Object)null) ? player.m_buildPieces.GetSelectedPiece() : null); Mod.Log.LogInfo((object)($"Build state: placeMode={((Character)player).InPlaceMode()} " + "ghost=" + (((Object)(object)player.m_placementGhost != (Object)null) ? ((Object)player.m_placementGhost).name : "<null>") + " " + string.Format("selected={0} active={1} ", ((Object)(object)val != (Object)null) ? ((Object)val).name : "<null>", IsActiveMode(player)) + $"hud={Hud.IsPieceSelectionVisible()} hasTarget={Mod.HasTargetHeight} target={Mod.TargetHeight:F2} radius={Mod.CurrentRadius:F1}")); } } private static void Postfix(Player __instance) { //IL_02ca: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } ZNetScenePatch.TryRegister(); TerrainOpRegistry.EnsureRegistered(); EnsurePieceKnown(__instance); if ((Object)(object)__instance.m_placementGhost != (Object)null && ((Character)__instance).InPlaceMode()) { LogBuildState(__instance); } if (!Mod.HasTargetHeight && !IsActiveMode(__instance)) { return; } if (!IsActiveMode(__instance) || Hud.IsPieceSelectionVisible()) { scrollAccumulator = 0f; keyRepeatTimer = 0f; if (!IsActiveMode(__instance)) { if (Mod.HasTargetHeight && Mod.DebugLogging.Value) { Piece val = (((Object)(object)__instance.m_buildPieces != (Object)null) ? __instance.m_buildPieces.GetSelectedPiece() : null); Mod.Log.LogInfo((object)("Reset hasTarget: ghost=" + (((Object)(object)__instance.m_placementGhost != (Object)null) ? ((Object)__instance.m_placementGhost).name : "<null>") + " " + string.Format("selected={0} hud={1}", ((Object)(object)val != (Object)null) ? ((Object)val).name : "<null>", Hud.IsPieceSelectionVisible()))); } Mod.HasTargetHeight = false; } return; } bool flag = ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true); bool flag2 = ZInput.GetKey((KeyCode)304, true) || ZInput.GetKey((KeyCode)303, 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 (flag2 && num > 0f) { Mod.TargetHeight = Mathf.Round(Mod.TargetHeight / num) * num; } } if (flag2) { 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 || player.m_knownRecipes == null || (player.m_knownRecipes.Contains("$terainer_level_round") && player.m_knownRecipes.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).m_inventory.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) && !player.m_knownRecipes.Contains(value)) { player.AddKnownPiece(piece.GetComponent<Piece>()); player.UpdateAvailablePiecesList(); } } } } } [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] public static class PieceTablePatch { private static void Prefix(PieceTable __instance, HashSet<string> knownRecipies) { if (__instance?.m_pieces != null) { int num = __instance.m_pieces.RemoveAll((GameObject p) => (Object)(object)p == (Object)null); if (num > 0) { Mod.Log.LogInfo((object)$"Убрано {num} уничтоженных пьесов из таблицы мотыги"); } } if (knownRecipies != null) { knownRecipies.Add("$terainer_level_round"); knownRecipies.Add("$terainer_level_square"); } } } [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), "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)__instance.m_placementGhost == (Object)null) { return true; } try { Vector3 position = __instance.m_placementGhost.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).m_inventory.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).m_inventory.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); } } internal static class TerrainOpRegistry { private static readonly List<TerrainOp> tracked = new List<TerrainOp>(); public static TerrainOp Find(GameObject go) { if ((Object)(object)go == (Object)null) { return null; } return go.GetComponent<TerrainOp>() ?? go.GetComponentInChildren<TerrainOp>(); } public static void Track(TerrainOp op) { if (!((Object)(object)op == (Object)null) && !tracked.Contains(op)) { tracked.Add(op); EnsureRegistered(); } } public static void Verify(TerrainOp op) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || (Object)(object)op == (Object)null) { return; } try { ZPackage val = new ZPackage(); op.m_settings.Serialize(val, ((Component)op).gameObject); val.SetPos(0); int num = val.ReadInt(); TerrainOp val2 = default(TerrainOp); if (instance.TryGetTerrainOp(num, ref val2) && (Object)(object)val2 == (Object)(object)op) { Mod.Log.LogInfo((object)$"TerrainOp '{((Object)((Component)op).gameObject).name}' (hash {num}): настройки доступны через ObjectDB — выравнивание применится"); } else { Mod.Log.LogError((object)$"TerrainOp '{((Object)((Component)op).gameObject).name}' (hash {num}): ObjectDB не отдаёт настройки — выравнивание НЕ сработает"); } } catch (Exception arg) { Mod.Log.LogError((object)$"TerrainOpRegistry.Verify: {arg}"); } } public static void Clear() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance != (Object)null) { foreach (TerrainOp item in tracked) { if (!((Object)(object)item == (Object)null)) { int prefabHash = instance.GetPrefabHash(((Component)item).gameObject); if (instance.m_terrainOpsByHash.TryGetValue(prefabHash, out var value) && (Object)(object)value == (Object)(object)item) { instance.m_terrainOpsByHash.Remove(prefabHash); } } } } tracked.Clear(); } public static void EnsureRegistered() { if (tracked.Count == 0) { return; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return; } for (int num = tracked.Count - 1; num >= 0; num--) { TerrainOp val = tracked[num]; if ((Object)(object)val == (Object)null) { tracked.RemoveAt(num); } else { int prefabHash = instance.GetPrefabHash(((Component)val).gameObject); if (!instance.m_terrainOpsByHash.TryGetValue(prefabHash, out var value) || (Object)(object)value != (Object)(object)val) { instance.m_terrainOpsByHash[prefabHash] = val; } } } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class ZNetScenePatch { private const int CylinderSegments = 32; private static Mesh cylinderMesh; private static GameObject roundClone; private static GameObject squareClone; private static float nextAttempt; private static void Postfix(ZNetScene __instance) { ObjectDB instance = ObjectDB.instance; Mod.Log.LogInfo((object)("ZNetScene.Awake: ObjectDB=" + (((Object)(object)instance == (Object)null) ? "нет" : "ок") + ", режимы=" + (((Object)(object)roundClone != (Object)null && (Object)(object)squareClone != (Object)null) ? "зарегистрированы" : "ждём"))); TryRegister(); } public static void Unregister() { try { PieceTable val = FindHoeTable(); if (val?.m_pieces != null) { int num = val.m_pieces.RemoveAll((GameObject p) => (Object)(object)p == (Object)null || ((Object)p).name.StartsWith("Terrainer_LevelToHeight", StringComparison.Ordinal)); if (num > 0) { Mod.Log.LogInfo((object)$"Снято {num} режимов с таблицы мотыги (выход из мира)"); } } Mod.GhostVisuals.Clear(); roundClone = null; squareClone = null; nextAttempt = 0f; TerrainOpRegistry.Clear(); } catch (Exception arg) { Mod.Log.LogError((object)$"ZNetScenePatch.Unregister: {arg}"); } } private static PieceTable FindHoeTable() { ObjectDB instance = ObjectDB.instance; if (instance == null) { return null; } GameObject itemPrefab = instance.GetItemPrefab("Hoe"); if (itemPrefab == null) { return null; } return itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces; } public static void TryRegister() { if (((Object)(object)roundClone != (Object)null && (Object)(object)squareClone != (Object)null) || Time.time < nextAttempt) { return; } nextAttempt = Time.time + 2f; try { Register(); } catch (Exception arg) { Mod.Log.LogError((object)$"ZNetScenePatch: {arg}"); } } private static void Register() { ZNetScene instance = ZNetScene.instance; ObjectDB instance2 = ObjectDB.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null) { return; } PieceTable val = FindHoeTable(); if ((Object)(object)val == (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)TerrainOpRegistry.Find(piece) != (Object)null) { val2 = piece; break; } } if ((Object)(object)val2 == (Object)null) { Mod.Log.LogWarning((object)"В таблице мотыги не найдено ни одного пьеса с TerrainOp, режимы не добавлены"); return; } roundClone = RegisterClone(instance, val, val2, "Terrainer_LevelToHeight", "$terainer_level_round", square: false); squareClone = RegisterClone(instance, val, val2, "Terrainer_LevelToHeightSquare", "$terainer_level_square", square: true); ModLoc.Register(); } private static GameObject 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 val2 = TerrainOpRegistry.Find(val); if ((Object)(object)val2 == (Object)null) { Mod.Log.LogWarning((object)("У пьеса-основы '" + ((Object)source).name + "' нет TerrainOp, режим '" + cloneName + "' не добавлен")); Object.Destroy((Object)(object)val); return null; } if ((Object)(object)((Component)val2).gameObject != (Object)(object)val && ((Object)((Component)val2).gameObject).name != cloneName) { ((Object)((Component)val2).gameObject).name = cloneName; } val2.m_settings.m_level = true; val2.m_settings.m_raise = false; val2.m_settings.m_smooth = false; val2.m_settings.m_square = square; val2.m_settings.m_levelRadius = Mod.Radius.Value; val2.m_settings.m_paintRadius = Mathf.Max(val2.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; } int prefabHash = scene.GetPrefabHash(val); if (!scene.m_namedPrefabs.ContainsKey(prefabHash)) { scene.m_namedPrefabs[prefabHash] = val; } if (!scene.m_prefabs.Contains(val)) { scene.m_prefabs.Add(val); } if (!table.m_pieces.Contains(val)) { table.m_pieces.Add(val); } TerrainOpRegistry.Track(val2); TerrainOpRegistry.Verify(val2); Mod.Log.LogInfo((object)($"Режим '{nameToken}' добавлен в таблицу мотыги (на основе {((Object)source).name}, радиус {Mod.Radius.Value}" + ((ghostVisualInfo != null) ? $", визуал {ghostVisualInfo.BaseRadius:F2} м" : ", визуал не найден") + ")")); return val; } 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_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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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)) { Vector3 lossyScale = ((Component)val).transform.lossyScale; float[] obj = new float[3] { num, 0f, 0f }; Bounds bounds = sharedMesh.bounds; obj[1] = ((Bounds)(ref bounds)).extents.x * Mathf.Abs(lossyScale.x); bounds = sharedMesh.bounds; obj[2] = ((Bounds)(ref bounds)).extents.z * Mathf.Abs(lossyScale.z); num = Mathf.Max(obj); } } 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_0282: 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_0293: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: 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 * (float)Math.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 * (float)Math.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 * (float)Math.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 = count2 + l; int item2 = count2 + l + 1; list3.Add(count); list3.Add(item2); list3.Add(item); list3.Add(count); list3.Add(item); list3.Add(item2); int item3 = count3 + l; int item4 = count3 + l + 1; int item5 = count4 + l; int item6 = count4 + l + 1; list3.Add(item3); list3.Add(item5); list3.Add(item6); list3.Add(item3); list3.Add(item6); list3.Add(item4); list3.Add(item3); list3.Add(item6); list3.Add(item5); list3.Add(item3); list3.Add(item4); list3.Add(item6); } cylinderMesh = new Mesh { vertices = list.ToArray(), normals = list2.ToArray(), triangles = list3.ToArray() }; cylinderMesh.RecalculateBounds(); ((Object)cylinderMesh).name = "TerrainerCylinder"; return cylinderMesh; } } [HarmonyPatch(typeof(ZNetScene), "OnDestroy")] public static class ZNetSceneDestroyPatch { private static void Postfix() { ZNetScenePatch.Unregister(); } } } 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; } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }