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 OhMyGrid v1.0.2
OhMyGrid.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("cgaggino")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Plant crops in circular/donut grids in Valheim.")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2+de619cbe58f38b63dc81270f12792949d48ea926")] [assembly: AssemblyProduct("OhMyGrid")] [assembly: AssemblyTitle("OhMyGrid")] [assembly: AssemblyVersion("1.0.2.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 OhMyGrid { public readonly struct GridPoint { public readonly float X; public readonly float Z; public GridPoint(float x, float z) { X = x; Z = z; } public override string ToString() { return $"({X:0.##}, {Z:0.##})"; } } public static class GridGenerator { public static IEnumerable<GridPoint> Donut(float centerX, float centerZ, float innerRadius, float outerRadius, float spacing) { if (spacing <= 0f) { throw new ArgumentOutOfRangeException("spacing"); } if (innerRadius < 0f) { throw new ArgumentOutOfRangeException("innerRadius"); } if (outerRadius < innerRadius) { throw new ArgumentOutOfRangeException("outerRadius"); } for (float r = innerRadius; r <= outerRadius + 0.0001f; r += spacing) { if (r <= 0.0001f) { yield return new GridPoint(centerX, centerZ); continue; } int n = Math.Max(1, (int)Math.Round(Math.PI * 2.0 * (double)r / (double)spacing)); for (int i = 0; i < n; i++) { double num = Math.PI * 2.0 * (double)i / (double)n; float x = centerX + (float)((double)r * Math.Cos(num)); float z = centerZ + (float)((double)r * Math.Sin(num)); yield return new GridPoint(x, z); } } } } [BepInPlugin("cgaggino.OhMyGrid", "OhMyGrid", "1.0.2")] public class OhMyGridPlugin : BaseUnityPlugin { public enum CenterMode { Player, Fixed, Cursor, CursorSnap } public enum GeometryMode { Off, Donut } [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static class TryPlacePiecePatch { [HarmonyPrefix] private static bool Prefix(Player __instance, Piece piece, ref bool __result) { if (_inDonutPlace) { return true; } if ((Object)(object)Instance == (Object)null) { return true; } if (Instance._geometryMode.Value == GeometryMode.Off) { return true; } GameObject val = PlacementGhostRef.Invoke(__instance); if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<Plant>() == (Object)null) { return true; } __result = Instance.PlaceDonut(__instance, piece, val); return false; } } [HarmonyPatch(typeof(Player), "Interact")] private static class InteractPatch { [HarmonyPrefix] private static bool Prefix(Player __instance, GameObject go, bool hold, bool alt) { if ((Object)(object)Instance == (Object)null || !Instance._massInteractEnabled.Value) { return true; } if (hold || alt) { return true; } if (!Input.GetKey((KeyCode)304) && !Input.GetKey((KeyCode)303)) { return true; } Instance.MassInteract(__instance); return false; } } public const string PluginGuid = "cgaggino.OhMyGrid"; public const string PluginName = "OhMyGrid"; public const string PluginVersion = "1.0.2"; private const float MinRadius = 0f; private const float MaxRadius = 32f; internal static ManualLogSource Log; private readonly Harmony _harmony = new Harmony("cgaggino.OhMyGrid"); private ConfigEntry<float> _innerRadius; private ConfigEntry<float> _outerRadius; private ConfigEntry<float> _spacing; private ConfigEntry<float> _autoSnapRadius; private ConfigEntry<CenterMode> _centerMode; private ConfigEntry<GeometryMode> _geometryMode; private ConfigEntry<bool> _showCostOverlay; private ConfigEntry<bool> _massInteractEnabled; private ConfigEntry<float> _massInteractRadius; private ConfigEntry<KeyboardShortcut> _dumpGridHotkey; private ConfigEntry<KeyboardShortcut> _cycleModeHotkey; private ConfigEntry<KeyboardShortcut> _cycleGeometryHotkey; private ConfigEntry<KeyboardShortcut> _increaseOuterHotkey; private ConfigEntry<KeyboardShortcut> _decreaseOuterHotkey; private ConfigEntry<KeyboardShortcut> _increaseInnerHotkey; private ConfigEntry<KeyboardShortcut> _decreaseInnerHotkey; private ConfigEntry<KeyboardShortcut> _massInteractHotkey; private Vector3 _fixedCenter; private bool _hasSnap; private readonly List<GameObject> _ghostClones = new List<GameObject>(); private readonly List<Piece> _ghostClonePieces = new List<Piece>(); private GameObject _lastSourceGhost; private Requirement[] _activeResources; private GameObject[] _activeGrownPrefabs; private string _activePieceLabel; private int _validPointsCount; private int _totalPointsCount; private int _affordableCount = int.MaxValue; private bool _cloneFailedForGhost; private readonly StringBuilder _overlaySb = new StringBuilder(256); private GUIStyle _overlayStyle; private static OhMyGridPlugin Instance; private static bool _inDonutPlace; private static readonly FieldRef<Player, GameObject> PlacementGhostRef = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost"); private static readonly Func<Plant, bool> HaveGrowSpaceCall = AccessTools.MethodDelegate<Func<Plant, bool>>(AccessTools.Method(typeof(Plant), "HaveGrowSpace", (Type[])null, (Type[])null), (object)null, true); private void Awake() { //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Instance = this; _innerRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Grid", "InnerRadius", 2f, "Inner radius (m) of the donut. Points inside this radius are skipped."); _outerRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Grid", "OuterRadius", 6f, "Outer radius (m) of the donut."); _spacing = ((BaseUnityPlugin)this).Config.Bind<float>("Grid", "Spacing", 1f, "Spacing (m) between rings and target spacing along each ring."); _autoSnapRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Center", "AutoSnapRadius", 12f, "Search radius (m) for CursorSnap. The donut snaps to the CENTROID of all Plants found within this radius from the cursor — so for concentric donut placement, this should be ≥ the outer radius of the existing donut you're aligning to."); _centerMode = ((BaseUnityPlugin)this).Config.Bind<CenterMode>("Center", "Mode", CenterMode.Player, "Current center mode. F7 cycles. Persists across sessions; if the saved value is Fixed it resets to Player on load (no persisted fixed center)."); if (_centerMode.Value == CenterMode.Fixed) { _centerMode.Value = CenterMode.Player; } _geometryMode = ((BaseUnityPlugin)this).Config.Bind<GeometryMode>("Geometry", "Mode", GeometryMode.Donut, "Active geometry. Off = vanilla single-plant (mod disabled). Donut = circular ring(s). F6 cycles. Persists."); _showCostOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("Overlay", "ShowCostOverlay", true, "Show an on-screen overlay with valid-point count, required seeds and expected yield while a plant ghost is active."); _massInteractEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("MassInteract", "Enabled", true, "Shift+E interacts with all Pickable / Fireplace / Smelter switches within MassInteractRadius."); _massInteractRadius = ((BaseUnityPlugin)this).Config.Bind<float>("MassInteract", "Radius", 5f, "Search radius (m) for Shift+E mass interact."); _dumpGridHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "DumpGrid", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Dump donut grid points around the donut center to the log."); _cycleModeHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "CycleCenterMode", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Cycle the donut center mode: Player → Fixed → Cursor → CursorSnap → Player."); _cycleGeometryHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "CycleGeometry", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Cycle the geometry: Off → Donut → Off."); _increaseOuterHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "IncreaseOuter", new KeyboardShortcut((KeyCode)93, Array.Empty<KeyCode>()), "Increase outer radius by one Spacing step."); _decreaseOuterHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "DecreaseOuter", new KeyboardShortcut((KeyCode)91, Array.Empty<KeyCode>()), "Decrease outer radius by one Spacing step."); _increaseInnerHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "IncreaseInner", new KeyboardShortcut((KeyCode)93, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Increase inner radius by one Spacing step."); _decreaseInnerHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "DecreaseInner", new KeyboardShortcut((KeyCode)91, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Decrease inner radius by one Spacing step."); _massInteractHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "MassInteract", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Mass-interact with all Pickable / Fireplace / Smelter switches in range."); Log.LogInfo((object)"OhMyGrid v1.0.2 loaded — hello from the grid."); _harmony.PatchAll(); } private void Update() { HandleHotkeys(); UpdateGhostClones(); } private void HandleHotkeys() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = _dumpGridHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { DumpGrid(); } value = _cycleModeHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { CycleMode(); } value = _cycleGeometryHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { CycleGeometry(); } float num = Mathf.Max(_spacing.Value, 0.01f); bool flag = false; value = _increaseInnerHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { AdjustRadius(_innerRadius, num); flag = true; } else { value = _decreaseInnerHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { AdjustRadius(_innerRadius, 0f - num); flag = true; } } if (!flag) { value = _increaseOuterHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { AdjustRadius(_outerRadius, num); } else { value = _decreaseOuterHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { AdjustRadius(_outerRadius, 0f - num); } } } if (!_massInteractEnabled.Value) { return; } value = _massInteractHotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { MassInteract(localPlayer); } } } private void CycleMode() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { GameObject ghost = PlacementGhostRef.Invoke(localPlayer); Vector3 donutCenter = GetDonutCenter(localPlayer, ghost); _centerMode.Value = NextMode(_centerMode.Value); string text; if (_centerMode.Value == CenterMode.Fixed) { _fixedCenter = donutCenter; text = $"Mode: Fixed @ ({_fixedCenter.x:0.##}, {_fixedCenter.z:0.##})"; } else { text = $"Mode: {_centerMode.Value}"; } Log.LogInfo((object)text); ShowHudMessage(text); } } private static void ShowHudMessage(string text) { MessageHud instance = MessageHud.instance; if (!((Object)(object)instance == (Object)null)) { instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true); } } private static CenterMode NextMode(CenterMode m) { return m switch { CenterMode.Player => CenterMode.Fixed, CenterMode.Fixed => CenterMode.Cursor, CenterMode.Cursor => CenterMode.CursorSnap, CenterMode.CursorSnap => CenterMode.Player, _ => CenterMode.Player, }; } private void CycleGeometry() { Log.LogInfo((object)$"Geometry: {NextGeometry(_geometryMode.Value)}"); _geometryMode.Value = NextGeometry(_geometryMode.Value); string text = $"Geometry: {_geometryMode.Value}"; Log.LogInfo((object)text); ShowHudMessage(text); } private static GeometryMode NextGeometry(GeometryMode g) { return g switch { GeometryMode.Off => GeometryMode.Donut, GeometryMode.Donut => GeometryMode.Off, _ => GeometryMode.Donut, }; } private void AdjustRadius(ConfigEntry<float> entry, float delta) { entry.Value = Mathf.Clamp(entry.Value + delta, 0f, 32f); Log.LogInfo((object)($"{((ConfigEntryBase)entry).Definition.Key} = {entry.Value:0.##}m " + $"(inner={_innerRadius.Value:0.##} outer={_outerRadius.Value:0.##})")); } private Vector3 GetDonutCenter(Player player, GameObject ghost) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (_centerMode.Value != CenterMode.CursorSnap && _hasSnap) { _hasSnap = false; } switch (_centerMode.Value) { case CenterMode.Fixed: return _fixedCenter; case CenterMode.Cursor: if (!((Object)(object)ghost != (Object)null)) { return ((Component)player).transform.position; } return ghost.transform.position; case CenterMode.CursorSnap: { Vector3 val = (((Object)(object)ghost != (Object)null) ? ghost.transform.position : ((Component)player).transform.position); Vector3? val2 = FindPlantCentroid(val, _autoSnapRadius.Value); if (val2.HasValue) { if (!_hasSnap) { Vector3 value = val2.Value; Log.LogInfo((object)$"AutoSnap → centroid ({value.x:0.##}, {value.z:0.##})."); ShowHudMessage("AutoSnap: locked"); _hasSnap = true; } return val2.Value; } if (_hasSnap) { Log.LogInfo((object)"AutoSnap: no plants in range."); ShowHudMessage("AutoSnap: lost"); _hasSnap = false; } return val; } default: return ((Component)player).transform.position; } } private static Vector3? FindPlantCentroid(Vector3 from, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_0043: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(from, radius); Vector3 val = Vector3.zero; int num = 0; HashSet<int> hashSet = new HashSet<int>(); for (int i = 0; i < array.Length; i++) { Plant componentInParent = ((Component)array[i]).GetComponentInParent<Plant>(); if (!((Object)(object)componentInParent == (Object)null)) { int instanceID = ((Object)componentInParent).GetInstanceID(); if (hashSet.Add(instanceID)) { val += ((Component)componentInParent).transform.position; num++; } } } if (num == 0) { return null; } return val / (float)num; } private void DumpGrid() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_00a4: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Log.LogInfo((object)"DumpGrid: no local player (in menu?)."); return; } Vector3 donutCenter = GetDonutCenter(localPlayer, PlacementGhostRef.Invoke(localPlayer)); Log.LogInfo((object)($"Donut grid @ ({donutCenter.x:0.##}, {donutCenter.z:0.##}) " + $"inner={_innerRadius.Value} outer={_outerRadius.Value} spacing={_spacing.Value}")); int num = 0; foreach (GridPoint item in GridGenerator.Donut(donutCenter.x, donutCenter.z, _innerRadius.Value, _outerRadius.Value, _spacing.Value)) { Log.LogInfo((object)$" [{num++}] {item}"); } Log.LogInfo((object)$"Donut grid: {num} points."); } private void UpdateGhostClones() { //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_048b: Unknown result type (might be due to invalid IL or missing references) //IL_0498: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0411: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { HideAllClones(); return; } if (_geometryMode.Value == GeometryMode.Off) { HideAllClones(); _lastSourceGhost = null; _activeResources = null; _activeGrownPrefabs = null; _activePieceLabel = null; _validPointsCount = 0; _totalPointsCount = 0; return; } GameObject val = PlacementGhostRef.Invoke(localPlayer); Plant val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Plant>() : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { HideAllClones(); _lastSourceGhost = null; _activeResources = null; _activeGrownPrefabs = null; _activePieceLabel = null; _validPointsCount = 0; _totalPointsCount = 0; return; } if ((Object)(object)val != (Object)(object)_lastSourceGhost) { DestroyAllClones(); _lastSourceGhost = val; _cloneFailedForGhost = false; Piece component = val.GetComponent<Piece>(); _activeResources = (((Object)(object)component != (Object)null) ? component.m_resources : null); _activeGrownPrefabs = val2.m_grownPrefabs; _activePieceLabel = (((Object)(object)component != (Object)null) ? LocalizeOrRaw(component.m_name) : ((Object)val).name); } Vector3 donutCenter = GetDonutCenter(localPlayer, val); List<GridPoint> list = new List<GridPoint>(); foreach (GridPoint item in GridGenerator.Donut(donutCenter.x, donutCenter.z, _innerRadius.Value, _outerRadius.Value, _spacing.Value)) { list.Add(item); } _totalPointsCount = list.Count; if (_cloneFailedForGhost) { HideAllClones(); _validPointsCount = 0; return; } if (_ghostClones.Count < list.Count) { int count = _ghostClones.Count; try { while (_ghostClones.Count < list.Count) { GameObject val3 = CreateGhostClone(val); _ghostClones.Add(val3); _ghostClonePieces.Add(val3.GetComponent<Piece>()); } Log.LogDebug((object)$"Ghost clones: {count} → {_ghostClones.Count} for '{((Object)val).name}' (active={val.activeSelf})"); } catch (Exception ex) { _cloneFailedForGhost = true; Log.LogError((object)("Ghost preview disabled for '" + ((Object)val).name + "': cloning threw " + ex.GetType().Name + ": " + ex.Message)); HideAllClones(); _validPointsCount = 0; return; } } Vector3 position = val.transform.position; Quaternion rotation = val.transform.rotation; Vector2 val4 = default(Vector2); ((Vector2)(ref val4))..ctor(((Component)localPlayer).transform.position.x, ((Component)localPlayer).transform.position.z); float num = localPlayer.m_maxPlaceDistance * localPlayer.m_maxPlaceDistance; bool needCultivatedGround = val2.m_needCultivatedGround; Piece component2 = val.GetComponent<Piece>(); int num2 = CountAffordable(localPlayer, component2); int num3 = 0; Vector3 val6 = default(Vector3); for (int i = 0; i < list.Count; i++) { GridPoint gridPoint = list[i]; GameObject val5 = _ghostClones[i]; float num4 = SampleGroundY(gridPoint.X, gridPoint.Z, position.y); ((Vector3)(ref val6))..ctor(gridPoint.X, num4, gridPoint.Z); val5.transform.position = val6; val5.transform.rotation = rotation; if (!val5.activeSelf) { val5.SetActive(true); } float num5 = gridPoint.X - val4.x; float num6 = gridPoint.Z - val4.y; bool num7 = num5 * num5 + num6 * num6 > num; bool flag = needCultivatedGround && !IsCultivatedAt(val6); val.transform.position = val6; bool flag2 = !HaveGrowSpaceCall(val2); bool flag3 = num7 || flag2 || flag; bool flag4 = !flag3 && num3 >= num2; if (!flag3) { num3++; } Piece val7 = _ghostClonePieces[i]; if ((Object)(object)val7 != (Object)null) { val7.SetInvalidPlacementHeightlight(flag3 || flag4); } } val.transform.position = position; val.transform.rotation = rotation; _validPointsCount = num3; _affordableCount = num2; for (int j = list.Count; j < _ghostClones.Count; j++) { if (_ghostClones[j].activeSelf) { _ghostClones[j].SetActive(false); } } } private static GameObject CreateGhostClone(GameObject ghost) { bool activeSelf = ghost.activeSelf; ghost.SetActive(false); GameObject val; try { val = Object.Instantiate<GameObject>(ghost); } finally { ghost.SetActive(activeSelf); } ((Object)val).name = ((Object)ghost).name + " (OhMyGrid ghost)"; Plant[] componentsInChildren = val.GetComponentsInChildren<Plant>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } ZNetView[] componentsInChildren2 = val.GetComponentsInChildren<ZNetView>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } Collider[] componentsInChildren3 = val.GetComponentsInChildren<Collider>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { componentsInChildren3[i].enabled = false; } val.SetActive(true); return val; } private static bool IsFreeBuild(Player player, Piece piece) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (player.NoCostCheat()) { return true; } ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance != (Object)null && (Object)(object)piece != (Object)null) { return instance.GetGlobalKey(piece.FreeBuildKey()); } return false; } private static int CountAffordable(Player player, Piece piece) { if ((Object)(object)piece == (Object)null || IsFreeBuild(player, piece)) { return int.MaxValue; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return 0; } int num = int.MaxValue; Requirement[] resources = piece.m_resources; if (resources == null) { return int.MaxValue; } foreach (Requirement val in resources) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_amount > 0) { SharedData val2 = val.m_resItem.m_itemData?.m_shared; if (val2 != null) { int num2 = inventory.CountItems(val2.m_name, -1, true); num = Mathf.Min(num, num2 / val.m_amount); } } } return num; } private static string DescribeStock(Player player, Piece piece) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null || (Object)(object)piece == (Object)null || piece.m_resources == null) { return "?"; } List<string> list = new List<string>(); Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { SharedData val2 = val?.m_resItem?.m_itemData?.m_shared; if (val2 != null && val.m_amount > 0) { list.Add($"{val2.m_name}×{val.m_amount} have {inventory.CountItems(val2.m_name, -1, true)}"); } } return string.Join(", ", list); } private static float SampleGroundY(float x, float z, float fallback) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null) { return fallback; } float result = default(float); if (instance.GetGroundHeight(new Vector3(x, 1000f, z), ref result)) { return result; } return fallback; } private void HideAllClones() { for (int i = 0; i < _ghostClones.Count; i++) { GameObject val = _ghostClones[i]; if ((Object)(object)val != (Object)null && val.activeSelf) { val.SetActive(false); } } } private void DestroyAllClones() { for (int i = 0; i < _ghostClones.Count; i++) { if ((Object)(object)_ghostClones[i] != (Object)null) { Object.Destroy((Object)(object)_ghostClones[i]); } } _ghostClones.Clear(); _ghostClonePieces.Clear(); } private bool PlaceDonut(Player player, Piece piece, GameObject ghost) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0242: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) Vector3 donutCenter = GetDonutCenter(player, ghost); Vector3 position = ghost.transform.position; Quaternion rotation = ghost.transform.rotation; Plant component = ghost.GetComponent<Plant>(); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(((Component)player).transform.position.x, ((Component)player).transform.position.z); float num = player.m_maxPlaceDistance * player.m_maxPlaceDistance; bool flag = (Object)(object)component != (Object)null && component.m_needCultivatedGround; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; bool flag2 = false; bool flag3 = IsFreeBuild(player, piece); int num6 = 0; int num7 = CountAffordable(player, piece); string text = DescribeStock(player, piece); _inDonutPlace = true; try { Vector3 val2 = default(Vector3); foreach (GridPoint item in GridGenerator.Donut(donutCenter.x, donutCenter.z, _innerRadius.Value, _outerRadius.Value, _spacing.Value)) { if (!flag3 && CountAffordable(player, piece) < num6 + 1) { flag2 = true; break; } float num8 = item.X - val.x; float num9 = item.Z - val.y; if (num8 * num8 + num9 * num9 > num) { num4++; continue; } float num10 = SampleGroundY(item.X, item.Z, position.y); ((Vector3)(ref val2))..ctor(item.X, num10, item.Z); if (flag && !IsCultivatedAt(val2)) { num5++; continue; } Vector3 val3 = val2; if ((Object)(object)component != (Object)null) { ghost.transform.position = val3; if (!HaveGrowSpaceCall(component)) { num3++; continue; } } try { player.PlacePiece(piece, val3, rotation, false, false); num2++; if (!flag3) { if (num6 > 0) { player.ConsumeResources(piece.m_resources, 0, -1, 1); } num6 = 1; } } catch (Exception ex) { Log.LogWarning((object)$"PlacePiece failed at ({item.X:0.##}, {item.Z:0.##}): {ex.Message}"); } } } finally { _inDonutPlace = false; ghost.transform.position = position; ghost.transform.rotation = rotation; } Log.LogInfo((object)($"Donut plant: planted={num2} noSpace={num3} tooFar={num4} notCultivated={num5}" + " | cost: affordableBefore=" + ((num7 == int.MaxValue) ? "∞" : num7.ToString()) + " stockBefore=[" + text + "] stockAfterMod=[" + DescribeStock(player, piece) + "]" + $" paidHere={((!flag3) ? Mathf.Max(num2 - 1, 0) : 0)} paidByVanilla={((num2 > 0 && !flag3) ? 1 : 0)}" + (flag3 ? " (free build)" : "") + (flag2 ? " (out of resources)" : ""))); return num2 > 0; } private static bool IsCultivatedAt(Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Heightmap val = Heightmap.FindHeightmap(pos); if ((Object)(object)val != (Object)null) { return val.IsCultivated(pos); } return false; } private void OnGUI() { //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Expected O, but got Unknown //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Expected O, but got Unknown //IL_0327: Expected O, but got Unknown //IL_0332: Unknown result type (might be due to invalid IL or missing references) if (!_showCostOverlay.Value || _totalPointsCount == 0 || (_activeResources == null && _activeGrownPrefabs == null)) { return; } StringBuilder overlaySb = _overlaySb; overlaySb.Length = 0; int num = Mathf.Min(_validPointsCount, _affordableCount); overlaySb.Append(_activePieceLabel ?? "Plant").Append(" · valid ").Append(_validPointsCount) .Append('/') .Append(_totalPointsCount); if (num < _validPointsCount) { overlaySb.Append(" · can afford ").Append(num); } overlaySb.Append("\nDonut · inner ").Append(_innerRadius.Value.ToString("0.##")).Append("m · outer ") .Append(_outerRadius.Value.ToString("0.##")) .Append("m · spacing ") .Append(_spacing.Value.ToString("0.##")) .Append('m'); if (_activeResources != null) { for (int i = 0; i < _activeResources.Length; i++) { Requirement val = _activeResources[i]; if (val == null || (Object)(object)val.m_resItem == (Object)null || val.m_amount <= 0) { continue; } SharedData val2 = val.m_resItem.m_itemData?.m_shared; if (val2 != null) { overlaySb.Append('\n').Append("Need: ").Append(LocalizeOrRaw(val2.m_name)) .Append(" × ") .Append(val.m_amount * num); Inventory val3 = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Humanoid)Player.m_localPlayer).GetInventory() : null); if (val3 != null) { overlaySb.Append(" (have ").Append(val3.CountItems(val2.m_name, -1, true)).Append(')'); } } } } if (_activeGrownPrefabs != null && _activeGrownPrefabs.Length != 0) { GameObject val4 = _activeGrownPrefabs[0]; Pickable val5 = (((Object)(object)val4 != (Object)null) ? val4.GetComponent<Pickable>() : null); if ((Object)(object)val5 != (Object)null && (Object)(object)val5.m_itemPrefab != (Object)null) { ItemDrop component = val5.m_itemPrefab.GetComponent<ItemDrop>(); SharedData val6 = ((!((Object)(object)component != (Object)null)) ? null : component.m_itemData?.m_shared); if (val6 != null) { overlaySb.Append('\n').Append("Yield: ~").Append(val5.m_amount * num) .Append(' ') .Append(LocalizeOrRaw(val6.m_name)); } } } if (_overlayStyle == null) { _overlayStyle = new GUIStyle(GUI.skin.box) { fontStyle = (FontStyle)1, fontSize = 14, alignment = (TextAnchor)4, wordWrap = false, padding = new RectOffset(12, 12, 8, 8) }; _overlayStyle.normal.textColor = Color.white; } string text = overlaySb.ToString(); GUIContent val7 = new GUIContent(text); float num2 = _overlayStyle.CalcSize(val7).x + 8f; float num3 = _overlayStyle.CalcHeight(val7, num2); float num4 = ((float)Screen.width - num2) * 0.5f; float num5 = 10f; GUI.Box(new Rect(num4, num5, num2, num3), text, _overlayStyle); } private static string LocalizeOrRaw(string key) { if (string.IsNullOrEmpty(key)) { return string.Empty; } Localization instance = Localization.instance; if (instance == null) { return key; } return instance.Localize(key); } private static string TryGetItemDisplayName(GameObject itemPrefab) { if ((Object)(object)itemPrefab == (Object)null) { return null; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); SharedData val = ((!((Object)(object)component != (Object)null)) ? null : component.m_itemData?.m_shared); if (val == null || string.IsNullOrEmpty(val.m_name)) { return ((Object)itemPrefab).name; } return LocalizeOrRaw(val.m_name); } private void MassInteract(Player player) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { ShowHudMessage("Shift+E: no target"); return; } float value = _massInteractRadius.Value; Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, value); HashSet<int> hashSet = new HashSet<int>(); int num = 0; Pickable componentInParent = hoverObject.GetComponentInParent<Pickable>(); string arg; if ((Object)(object)componentInParent != (Object)null) { GameObject itemPrefab = componentInParent.m_itemPrefab; string text = TryGetItemDisplayName(itemPrefab); arg = "picked " + (text ?? "items"); Log.LogInfo((object)string.Format("Shift+E target: pickable '{0}' radius={1}", ((Object)(object)itemPrefab != (Object)null) ? ((Object)itemPrefab).name : "?", value)); for (int i = 0; i < array.Length; i++) { Pickable componentInParent2 = ((Component)array[i]).GetComponentInParent<Pickable>(); if ((Object)(object)componentInParent2 == (Object)null || !hashSet.Add(((Object)componentInParent2).GetInstanceID()) || (Object)(object)componentInParent2.m_itemPrefab != (Object)(object)itemPrefab) { continue; } try { if (componentInParent2.Interact((Humanoid)(object)player, false, false)) { num++; } } catch (Exception ex) { Log.LogWarning((object)("MassInteract pickable: " + ex.Message)); } } } else { Fireplace fire = hoverObject.GetComponentInParent<Fireplace>(); if (fire != null) { arg = "fueled " + LocalizeOrRaw(fire.m_name); num = FeedUntilRefused(() => fire.Interact((Humanoid)(object)player, false, true), "fireplace"); Log.LogInfo((object)$"Shift+E target: fireplace '{fire.m_name}' fuel={fire.m_fuelItem?.m_itemData?.m_shared?.m_name} added={num}"); } else { Smelter componentInParent3 = hoverObject.GetComponentInParent<Smelter>(); if (componentInParent3 == null) { ShowHudMessage("Shift+E: target not supported"); return; } arg = "fed " + LocalizeOrRaw(componentInParent3.m_name); Switch componentInParent4 = hoverObject.GetComponentInParent<Switch>(); bool flag = (Object)(object)componentInParent4 != (Object)null && (Object)(object)componentInParent4 == (Object)(object)componentInParent3.m_addOreSwitch; bool flag2 = (Object)(object)componentInParent4 != (Object)null && (Object)(object)componentInParent4 == (Object)(object)componentInParent3.m_addWoodSwitch; int num2 = 0; int num3 = 0; if ((Object)(object)componentInParent3.m_addOreSwitch != (Object)null && !flag2) { Switch sw = componentInParent3.m_addOreSwitch; num2 = FeedUntilRefused(() => sw.Interact((Humanoid)(object)player, false, false), "smelter ore"); } if ((Object)(object)componentInParent3.m_addWoodSwitch != (Object)null && !flag) { Switch sw2 = componentInParent3.m_addWoodSwitch; num3 = FeedUntilRefused(() => sw2.Interact((Humanoid)(object)player, false, false), "smelter fuel"); } num = num2 + num3; Log.LogInfo((object)string.Format("Shift+E target: smelter '{0}' slot={1} oreAdded={2} fuelAdded={3} fuel={4}", componentInParent3.m_name, flag ? "ore" : (flag2 ? "fuel" : "both"), num2, num3, componentInParent3.m_fuelItem?.m_itemData?.m_shared?.m_name)); } } string text2 = $"Shift+E: {arg} × {num}"; Log.LogInfo((object)text2); ShowHudMessage(text2); } private static int FeedUntilRefused(Func<bool> feedOnce, string what) { int i = 0; try { for (; i < 64; i++) { if (!feedOnce()) { break; } } } catch (Exception ex) { Log.LogWarning((object)("MassInteract " + what + ": " + ex.Message)); } return i; } private void OnDestroy() { DestroyAllClones(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } }