Decompiled source of PlsCollapse v1.0.0

PlsCollapse.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("PlsCollapse")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PlsCollapse")]
[assembly: AssemblyTitle("PlsCollapse")]
[assembly: AssemblyVersion("1.0.0.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 PlsCollapse
{
	[BepInPlugin("Iteravio.PlsCollapse", "PlsCollapse", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Iteravio.PlsCollapse";

		public const string PluginName = "PlsCollapse";

		public const string PluginVersion = "1.0.0";

		private const string SettingsSection = "Pls Collapse";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> FootingPercent;

		internal static ConfigEntry<int> BoxSizePercent;

		internal static ConfigEntry<bool> LogChecks;

		private void Awake()
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Pls Collapse", "Enabled", true, Synced("Rock pieces fall once nothing really holds them up. Vanilla: off.", null, 3));
			FootingPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Pls Collapse", "PercentFootingNeeded", 15, Synced("How much of a rock's footing must remain for it to stay up; mine past that and the whole rock drops. 0 turns this off. Vanilla: off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 90), 2));
			BoxSizePercent = ((BaseUnityPlugin)this).Config.Bind<int>("Pls Collapse", "PercentSupportBoxSize", 100, Synced("Size of each piece's support box relative to the piece. Lower makes pieces fall sooner.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(25, 150), 1));
			LogChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogSupportChecks", false, "Log a line each time a rock checks support (when one of its pieces breaks). Local only, not synced.");
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new StatusCommand());
			new Harmony("Iteravio.PlsCollapse").PatchAll();
			if (RockSupport.Ready)
			{
				Log.LogInfo((object)("Ready: " + GetSettingsSummary()));
			}
			else
			{
				Log.LogError((object)"MineRock5 internals not found (game update?). Rocks keep vanilla support.");
			}
		}

		private static ConfigDescription Synced(string description, AcceptableValueBase range, int order)
		{
			//IL_000a: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			return new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				Order = order
			} });
		}

		internal static string GetSettingsSummary()
		{
			if (!Enabled.Value)
			{
				return "off (vanilla support)";
			}
			return $"on, footing {FootingPercent.Value}%, boxes {BoxSizePercent.Value}%";
		}

		internal static string GetSettingsSource()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return "local config";
			}
			if (!instance.IsServer())
			{
				return "synced from host";
			}
			return "this machine (host/solo)";
		}
	}
	internal static class RockSupport
	{
		private sealed class FootingMemory
		{
			public int Max;
		}

		private const float GroundContactTolerance = 0.3f;

		private const int GroundSamplesPerAxis = 5;

		private const float GroundSampleInset = 0.02f;

		internal static readonly bool Ready;

		internal static int Checks;

		internal static int Collapsed;

		internal static int FootingDrops;

		private static readonly FieldInfo HitAreasField;

		private static readonly FieldInfo RayMaskField;

		private static readonly FieldInfo ColliderField;

		private static readonly FieldInfo HealthField;

		private static readonly FieldInfo SupportedField;

		private static readonly FieldInfo BoundField;

		private static readonly FieldInfo BoundPosField;

		private static readonly FieldInfo BoundRotField;

		private static readonly FieldInfo BoundSizeField;

		private static readonly Func<MineRock5, Collider, bool> GetSupport;

		private static readonly Collider[] Overlaps;

		private static int terrainLayer;

		private static int terrainMask;

		private static readonly ConditionalWeakTable<MineRock5, FootingMemory> Footing;

		static RockSupport()
		{
			Overlaps = (Collider[])(object)new Collider[128];
			terrainLayer = -1;
			Footing = new ConditionalWeakTable<MineRock5, FootingMemory>();
			try
			{
				Type type = AccessTools.Inner(typeof(MineRock5), "HitArea");
				Type type2 = AccessTools.Inner(typeof(MineRock5), "BoundData");
				HitAreasField = AccessTools.Field(typeof(MineRock5), "m_hitAreas");
				RayMaskField = AccessTools.Field(typeof(MineRock5), "m_rayMask");
				ColliderField = AccessTools.Field(type, "m_collider");
				HealthField = AccessTools.Field(type, "m_health");
				SupportedField = AccessTools.Field(type, "m_supported");
				BoundField = AccessTools.Field(type, "m_bound");
				BoundPosField = AccessTools.Field(type2, "m_pos");
				BoundRotField = AccessTools.Field(type2, "m_rot");
				BoundSizeField = AccessTools.Field(type2, "m_size");
				GetSupport = (Func<MineRock5, Collider, bool>)Delegate.CreateDelegate(typeof(Func<MineRock5, Collider, bool>), AccessTools.Method(typeof(MineRock5), "GetSupport", (Type[])null, (Type[])null));
				Ready = HitAreasField != null && RayMaskField != null && ColliderField != null && HealthField != null && SupportedField != null && BoundField != null && BoundPosField != null && BoundRotField != null && BoundSizeField != null;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
				Ready = false;
			}
		}

		internal static void FitBounds(MineRock5 rock)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00c9: 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)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready || !Plugin.Enabled.Value)
			{
				return;
			}
			float num = (float)Plugin.BoxSizePercent.Value / 100f;
			Vector3 position = ((Component)rock).transform.position;
			foreach (object item in (IList)HitAreasField.GetValue(rock))
			{
				Collider val = (Collider)ColliderField.GetValue(item);
				if (!((Object)(object)val == (Object)null) && TryGetLocalBounds(val, out var bounds))
				{
					Transform transform = ((Component)val).transform;
					Vector3 lossyScale = transform.lossyScale;
					Vector3 val2 = Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z))) * num;
					object value = BoundField.GetValue(item);
					BoundPosField.SetValue(value, transform.TransformPoint(((Bounds)(ref bounds)).center) - position);
					BoundRotField.SetValue(value, transform.rotation);
					BoundSizeField.SetValue(value, val2);
					BoundField.SetValue(item, value);
				}
			}
		}

		private static bool TryGetLocalBounds(Collider collider, out Bounds bounds)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			MeshCollider val = (MeshCollider)(object)((collider is MeshCollider) ? collider : null);
			if (val == null)
			{
				BoxCollider val2 = (BoxCollider)(object)((collider is BoxCollider) ? collider : null);
				if (val2 != null)
				{
					bounds = new Bounds(val2.center, val2.size);
					return true;
				}
				SphereCollider val3 = (SphereCollider)(object)((collider is SphereCollider) ? collider : null);
				if (val3 != null)
				{
					bounds = new Bounds(val3.center, Vector3.one * (val3.radius * 2f));
					return true;
				}
				CapsuleCollider val4 = (CapsuleCollider)(object)((collider is CapsuleCollider) ? collider : null);
				if (val4 != null)
				{
					Vector3 val5 = Vector3.one * (val4.radius * 2f);
					((Vector3)(ref val5))[val4.direction] = Mathf.Max(val4.height, val4.radius * 2f);
					bounds = new Bounds(val4.center, val5);
					return true;
				}
			}
			else if ((Object)(object)val.sharedMesh != (Object)null)
			{
				bounds = val.sharedMesh.bounds;
				return true;
			}
			bounds = default(Bounds);
			return false;
		}

		internal static void AfterUpdateSupport(MineRock5 rock)
		{
			if (!Ready)
			{
				return;
			}
			IList list = (IList)HitAreasField.GetValue(rock);
			string text = (Plugin.Enabled.Value ? Recompute(rock, list) : "vanilla");
			int num = 0;
			int num2 = 0;
			foreach (object item in list)
			{
				if (!((float)HealthField.GetValue(item) <= 0f))
				{
					num++;
					if ((bool)SupportedField.GetValue(item))
					{
						num2++;
					}
				}
			}
			Checks++;
			Collapsed += num - num2;
			if (Plugin.LogChecks.Value)
			{
				Plugin.Log.LogInfo((object)($"{GetPrefabName((Component)(object)rock)}: {num2}/{num} pieces supported ({text}), " + $"{num - num2} collapsing ({Plugin.GetSettingsSummary()})"));
			}
		}

		private static string Recompute(MineRock5 rock, IList areas)
		{
			//IL_004c: 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)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: 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_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: 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_01c5: Unknown result type (might be due to invalid IL or missing references)
			if (terrainLayer < 0)
			{
				terrainLayer = LayerMask.NameToLayer("terrain");
				terrainMask = LayerMask.GetMask(new string[1] { "terrain" });
			}
			int num = (int)RayMaskField.GetValue(null);
			Vector3 position = ((Component)rock).transform.position;
			int count = areas.Count;
			bool[] array = new bool[count];
			Dictionary<Collider, int> dictionary = new Dictionary<Collider, int>(count);
			for (int i = 0; i < count; i++)
			{
				array[i] = (float)HealthField.GetValue(areas[i]) > 0f;
				Collider val = (Collider)ColliderField.GetValue(areas[i]);
				if ((Object)(object)val != (Object)null)
				{
					dictionary[val] = i;
				}
			}
			bool[] array2 = new bool[count];
			List<int>[] array3 = new List<int>[count];
			int num2 = 0;
			int num3 = 0;
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
			float num4 = 0f;
			float num5 = 0f;
			for (int j = 0; j < count; j++)
			{
				array3[j] = new List<int>();
				if (!array[j])
				{
					continue;
				}
				Collider val2 = (Collider)ColliderField.GetValue(areas[j]);
				object value = BoundField.GetValue(areas[j]);
				Vector3 val3 = (Vector3)BoundSizeField.GetValue(value);
				Bounds bounds = val2.bounds;
				Vector3 extents = ((Bounds)(ref bounds)).extents;
				num4 += val3.x * val3.y * val3.z;
				num5 += extents.x * extents.y * extents.z;
				int num6 = Physics.OverlapBoxNonAlloc(position + (Vector3)BoundPosField.GetValue(value), val3, Overlaps, (Quaternion)BoundRotField.GetValue(value), num);
				bool flag = false;
				string text = null;
				for (int k = 0; k < num6; k++)
				{
					Collider val4 = Overlaps[k];
					if (!((Object)(object)val4 == (Object)(object)val2) && !((Object)(object)val4.attachedRigidbody != (Object)null) && !val4.isTrigger)
					{
						int value2;
						if (((Component)val4).gameObject.layer == terrainLayer)
						{
							flag = true;
						}
						else if (dictionary.TryGetValue(val4, out value2))
						{
							array3[j].Add(value2);
						}
						else if (text == null && GetSupport(rock, val4))
						{
							text = GetPrefabName((Component)(object)val4);
						}
					}
				}
				if (flag && RestsOnGround(val2))
				{
					array2[j] = true;
					num2++;
				}
				else if (text != null)
				{
					array2[j] = true;
					dictionary2[text] = ((!dictionary2.TryGetValue(text, out var value3)) ? 1 : (value3 + 1));
				}
				else if (flag)
				{
					num3++;
				}
			}
			bool[] supported = (bool[])array2.Clone();
			bool flag2 = true;
			while (flag2)
			{
				flag2 = false;
				for (int l = 0; l < count; l++)
				{
					if (array[l] && !supported[l] && array3[l].Any((int num11) => supported[num11]))
					{
						supported[l] = true;
						flag2 = true;
					}
				}
			}
			int num7 = array2.Count((bool a) => a);
			FootingMemory value4 = Footing.GetValue(rock, (MineRock5 _) => new FootingMemory());
			value4.Max = Math.Max(value4.Max, num7);
			bool flag3 = num7 * 100 < value4.Max * Plugin.FootingPercent.Value;
			if (flag3)
			{
				FootingDrops++;
			}
			int num8 = 0;
			for (int num9 = 0; num9 < count; num9++)
			{
				if (array[num9])
				{
					bool flag4 = supported[num9] && !flag3;
					SupportedField.SetValue(areas[num9], flag4);
					if (flag4 && !array2[num9])
					{
						num8++;
					}
				}
			}
			string text2 = ((dictionary2.Count == 0) ? "" : (", on " + string.Join(", ", dictionary2.Select((KeyValuePair<string, int> kv) => $"{kv.Key} x{kv.Value}"))));
			float num10 = ((num5 > 0f) ? (num4 / num5) : 1f);
			return $"{num2} on ground{text2}, {num3} only grazing the ground, {num8} via other pieces; " + string.Format("footing {0}/{1}{2}; boxes {3:0.00}x vanilla volume", num7, value4.Max, flag3 ? " too little, dropping all" : "", num10);
		}

		private static bool RestsOnGround(Collider piece)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: 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)
			Bounds bounds = piece.bounds;
			float num = ((Bounds)(ref bounds)).max.y + 1f;
			RaycastHit val = default(RaycastHit);
			RaycastHit val2 = default(RaycastHit);
			for (int i = 0; i < 5; i++)
			{
				for (int j = 0; j < 5; j++)
				{
					float num2 = Mathf.Lerp(((Bounds)(ref bounds)).min.x, ((Bounds)(ref bounds)).max.x, Mathf.Lerp(0.02f, 0.98f, (float)i / 4f));
					float num3 = Mathf.Lerp(((Bounds)(ref bounds)).min.z, ((Bounds)(ref bounds)).max.z, Mathf.Lerp(0.02f, 0.98f, (float)j / 4f));
					if (Physics.Raycast(new Vector3(num2, num, num3), Vector3.down, ref val, num - ((Bounds)(ref bounds)).min.y + 50f, terrainMask))
					{
						float num4 = Mathf.Min(((RaycastHit)(ref val)).point.y, ((Bounds)(ref bounds)).min.y) - 1f;
						if (piece.Raycast(new Ray(new Vector3(num2, num4, num3), Vector3.up), ref val2, num - num4) && ((RaycastHit)(ref val2)).point.y <= ((RaycastHit)(ref val)).point.y + 0.3f)
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		private static string GetPrefabName(Component component)
		{
			ZNetView componentInParent = component.GetComponentInParent<ZNetView>();
			ZDO val = (Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.GetZDO() : null);
			GameObject val2 = ((val != null && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.GetPrefab(val.GetPrefab()) : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return ((Object)component.transform.root).name.Replace("(Clone)", "");
			}
			return ((Object)val2).name;
		}
	}
	[HarmonyPatch(typeof(MineRock5), "UpdateSupport")]
	internal static class UpdateSupportPatch
	{
		private static void Prefix(ref bool ___m_haveSetupBounds)
		{
			___m_haveSetupBounds = false;
		}

		private static void Postfix(MineRock5 __instance)
		{
			RockSupport.AfterUpdateSupport(__instance);
		}
	}
	[HarmonyPatch(typeof(MineRock5), "SetupColliders")]
	internal static class SetupCollidersPatch
	{
		private static void Postfix(MineRock5 __instance)
		{
			RockSupport.FitBounds(__instance);
		}
	}
	internal class StatusCommand : ConsoleCommand
	{
		public override string Name => "plscollapse";

		public override string Help => "Show PlsCollapse settings in effect and this session's rock collapse counts";

		public override void Run(string[] args, Terminal context)
		{
			if (!((Object)(object)context == (Object)null))
			{
				context.AddString("PlsCollapse 1.0.0");
				if (!RockSupport.Ready)
				{
					context.AddString("Inactive: game internals not found, rocks use vanilla support. See the BepInEx log.");
					return;
				}
				context.AddString("Settings (" + Plugin.GetSettingsSource() + "): " + Plugin.GetSettingsSummary());
				context.AddString($"This session on this machine: {RockSupport.Checks} support checks, " + $"{RockSupport.Collapsed} pieces collapsed, {RockSupport.FootingDrops} rocks dropped by the footing rule");
			}
		}
	}
}