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 BetterStability v1.0.0
plugins/BetterStability.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BetterStability.Core; using BetterStability.Dev; using BetterStability.Gameplay; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.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 BetterStability { public static class Cfg { public static ConfigEntry<bool> UseTiers; public static readonly Dictionary<Tier, ConfigEntry<string>> TierText = new Dictionary<Tier, ConfigEntry<string>>(); public static ConfigEntry<string> TierOverrides; public static ConfigEntry<bool> Anchoring; public static ConfigEntry<float> FullHoldShare; public static ConfigEntry<float> MinDepth; public static ConfigEntry<string> HostTiers; public static ConfigEntry<bool> ShowSupport; private static readonly TierValues[] _values = new TierValues[Strength.All.Length]; private static readonly string[] _valueText = new string[Strength.All.Length]; private static string _overrideText; private static string _hostText; private static Dictionary<string, Tier> _overrides = new Dictionary<string, Tier>(StringComparer.OrdinalIgnoreCase); private static readonly bool[] _hosts = new bool[Strength.All.Length]; public static Dictionary<string, Tier> Overrides { get { string text = TierOverrides?.Value ?? ""; if (text != _overrideText) { List<string> list = new List<string>(); _overrides = Strength.ParseOverrides(text, list); _overrideText = text; if (list.Count > 0) { ManualLogSource log = BetterStabilityPlugin.Log; if (log != null) { log.LogWarning((object)("TierOverrides: ignored " + string.Join(", ", list) + " (expected prefab:Tier)")); } } } return _overrides; } } public static event Action Changed; public static void Bind(ConfigFile c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; UseTiers = c.Bind<bool>("1 - Materials", "UseTiers", true, new ConfigDescription("Use BetterStability's material values below instead of the game's own. Off: every material holds as in vanilla (anchoring still works).", (AcceptableValueBase)null, new object[1] { val })); Tier[] all = Strength.All; for (int i = 0; i < all.Length; i++) { Tier tier = all[i]; ConfigEntry<string> val2 = c.Bind<string>("1 - Materials", tier.ToString(), Strength.Default(tier).ToString(), new ConfigDescription(TierDescription(tier), (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, Order = 0 - tier } })); TierText[tier] = val2; val2.SettingChanged += delegate { Cfg.Changed?.Invoke(); }; } TierOverrides = c.Bind<string>("1 - Materials", "TierOverrides", "", new ConfigDescription("Give single pieces another material as \"prefab:Tier\", comma separated, for example \"woodiron_beam:Flametal, piece_mymod_wall:BlackMarble\". Tiers: " + string.Join(", ", Strength.All) + ".", (AcceptableValueBase)null, new object[1] { val })); Anchoring = c.Bind<bool>("2 - Anchoring", "Anchoring", true, new ConfigDescription("Pieces set into rock, the ground or masonry hold by it: deep enough in rock or a foundation wall they stand as on the ground; in higher masonry they take that wall's support without loss over distance.", (AcceptableValueBase)null, new object[1] { val })); FullHoldShare = c.Bind<float>("2 - Anchoring", "FullHoldShare", 0.33f, new ConfigDescription("Part of a piece that must sit inside the rock or wall for a full hold: 0.33 means a third (a 2 m beam needs about 0.7 m inside). Less holds in proportion.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), new object[1] { val })); MinDepth = c.Bind<float>("2 - Anchoring", "MinDepth", 0.25f, new ConfigDescription("A piece reaching less than this many metres into rock or a wall is not anchored at all (it only touches, as in vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { val })); HostTiers = c.Bind<string>("2 - Anchoring", "HostTiers", "Stone, BlackMarble, Grausten, Ancient", new ConfigDescription("Materials that hold a piece set into them, comma separated. Rock, the ground and fixed world structures always hold.", (AcceptableValueBase)null, new object[1] { val })); ShowSupport = c.Bind<bool>("3 - Client", "ShowSupport", true, "With the hammer in hand, the piece you look at shows its material, its support in percent and how it is anchored."); UseTiers.SettingChanged += delegate { Cfg.Changed?.Invoke(); }; Anchoring.SettingChanged += delegate { Cfg.Changed?.Invoke(); }; ConfigEntry<string>[] array = new ConfigEntry<string>[2] { TierOverrides, HostTiers }; for (int i = 0; i < array.Length; i++) { array[i].SettingChanged += delegate { Cfg.Changed?.Invoke(); }; } ConfigEntry<float>[] array2 = new ConfigEntry<float>[2] { FullHoldShare, MinDepth }; for (int i = 0; i < array2.Length; i++) { array2[i].SettingChanged += delegate { Cfg.Changed?.Invoke(); }; } } public static void RaiseChanged() { Cfg.Changed?.Invoke(); } private static string TierDescription(Tier t) { TierValues tierValues = Strength.Vanilla(t); object obj; if (t != Tier.Flametal) { TierValues tierValues2 = tierValues; obj = "vanilla " + tierValues2.ToString(); } else { obj = "vanilla files flametal pieces under Iron"; } string arg = (string)obj; return $"{t}: max support, min support, loss per metre upwards, loss per metre sideways ({arg}). " + "A grounded piece has max; each link to a neighbour keeps 1 - loss × distance of its support; below min it breaks."; } public static TierValues Values(Tier t) { ConfigEntry<string> value; string text = (TierText.TryGetValue(t, out value) ? value.Value : null); if ((object)text != _valueText[(int)t]) { _valueText[(int)t] = text; if (!Strength.TryParse(text, out _values[(int)t])) { _values[(int)t] = Strength.Default(t); ManualLogSource log = BetterStabilityPlugin.Log; if (log != null) { log.LogWarning((object)$"{t}: could not read \"{text}\" (expected max, min, up, out); using {_values[(int)t]}."); } } } return _values[(int)t]; } public static bool IsHost(Tier t) { string text = HostTiers?.Value ?? ""; if (text != _hostText) { _hostText = text; Array.Clear(_hosts, 0, _hosts.Length); string[] array = text.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { if (Enum.TryParse<Tier>(text2.Trim(), ignoreCase: true, out var result) && Enum.IsDefined(typeof(Tier), result)) { _hosts[(int)result] = true; continue; } ManualLogSource log = BetterStabilityPlugin.Log; if (log != null) { log.LogWarning((object)("HostTiers: unknown material " + text2)); } } } return _hosts[(int)t]; } } public static class Lang { private static readonly (string key, string en, string de)[] Texts = new(string, string, string)[21] { ("bs_tier_wood", "Wood", "Holz"), ("bs_tier_stone", "Stone", "Stein"), ("bs_tier_iron", "Iron", "Eisen"), ("bs_tier_corewood", "Core wood", "Kernholz"), ("bs_tier_blackmarble", "Black marble", "Schwarzmarmor"), ("bs_tier_grausten", "Grausten", "Grausten"), ("bs_tier_ancient", "Ancient stone", "Uraltes Gestein"), ("bs_tier_ice", "Ice", "Eis"), ("bs_tier_timberwood", "Timber", "Bauholz"), ("bs_tier_flametal", "Flametal", "Flammenmetall"), ("bs_hover_support", "{0} · support {1} %", "{0} · Halt {1} %"), ("bs_hover_support_full", "{0} · full support", "{0} · voller Halt"), ("bs_host_ground", "the ground", "den Boden"), ("bs_host_rock", "rock", "den Fels"), ("bs_hover_bedrock", "Set {0} m into {1} – as firm as the ground", "{0} m tief in {1} gesetzt – fest wie der Boden"), ("bs_hover_foundation", "Set {0} m into a foundation wall – as firm as the ground", "{0} m tief in eine Grundmauer gesetzt – fest wie der Boden"), ("bs_hover_masonry", "Set {0} m into {1} – carried by the wall", "{0} m tief in {1} gesetzt – die Mauer trägt mit"), ("bs_host_foundation", "a foundation wall", "eine Grundmauer"), ("bs_hover_set", "Set {0} m into {1}", "{0} m tief in {1} gesetzt"), ("bs_hover_partial", "Holds {0} % – {1} m in would hold fully", "Hält zu {0} % – {1} m tief hielte es ganz"), ("bs_hover_shallow", "Only {0} m in {1} – too shallow to hold", "Nur {0} m in {1} – zu flach, um zu halten") }; public static void Register() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); Dictionary<string, string> dictionary = new Dictionary<string, string>(); Dictionary<string, string> dictionary2 = new Dictionary<string, string>(); (string, string, string)[] texts = Texts; for (int i = 0; i < texts.Length; i++) { (string, string, string) tuple = texts[i]; string item = tuple.Item1; string item2 = tuple.Item2; string item3 = tuple.Item3; dictionary[item] = item2; dictionary2[item] = item3; } Pieces.AddTexts(dictionary, dictionary2); string text = "English"; localization.AddTranslation(ref text, dictionary); text = "German"; localization.AddTranslation(ref text, dictionary2); } public static string T(string token, params object[] args) { string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + token) : token); if (args == null || args.Length == 0) { return text; } try { return string.Format(text, args); } catch (FormatException) { return text; } } public static string L(string token) { if (Localization.instance == null) { return token; } return Localization.instance.Localize(token); } public static string TierName(Tier t) { return T("bs_tier_" + t.ToString().ToLowerInvariant()); } } public static class Pieces { public static void Init() { } public static void AddTexts(Dictionary<string, string> en, Dictionary<string, string> de) { } } [BepInPlugin("zap.betterstability", "Better Stability", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class BetterStabilityPlugin : BaseUnityPlugin { public const string PluginGuid = "zap.betterstability"; public const string PluginName = "Better Stability"; public const string PluginVersion = "1.0.0"; private Harmony _harmony; public static BetterStabilityPlugin Instance { get; private set; } public static string PluginDir { get; private set; } public static ManualLogSource Log { get; private set; } public static string AssetPath(string fileName) { return Path.Combine(PluginDir ?? "", "Assets", fileName); } private void Awake() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); Cfg.Bind(((BaseUnityPlugin)this).Config); Lang.Register(); Pieces.Init(); _harmony = new Harmony("zap.betterstability"); _harmony.PatchAll(typeof(BetterStabilityPlugin).Assembly); SelfTest.TryStart(((Component)this).gameObject); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace BetterStability.Gameplay { public struct Anchor { public HostKind Kind; public float Share; public float Depth; public float Length; public WearNTear Host; public Collider Collider; public Collider[] Rocks; public string Name; } public static class Anchors { [HarmonyPatch(typeof(WearNTear), "UpdateSupport")] private static class HoldByAnchor { private static bool Prefix(WearNTear __instance, out float __state) { __state = 0f; _updating = __instance; if (Stability.Bypass || !Cfg.Anchoring.Value || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid()) { return true; } try { PieceInfo pieceInfo = Stability.Info(__instance); if (Time.time - pieceInfo.LastHold > 30f) { pieceInfo.AnchorDirty = true; pieceInfo.GroundedDirty = true; } pieceInfo.LastHold = Time.time; float max = Stability.ValuesOf(__instance).Max; __state = Hold(__instance, pieceInfo, max, out var _); if (__state >= max - 0.001f && !__instance.m_clearCachedSupport) { SetSupport(__instance, max); return false; } } catch (Exception ex) { BetterStabilityPlugin.Log.LogWarning((object)("Anchor check failed on " + ((Object)__instance).name + ": " + ex.Message)); __state = 0f; } return true; } private static void Postfix(WearNTear __instance, float __state) { _updating = null; if (!(__state <= 0f) && !((Object)(object)__instance == (Object)null) && !(__state <= __instance.m_support)) { SetSupport(__instance, Mathf.Min(__state, Stability.ValuesOf(__instance).Max)); } } private static Exception Finalizer(Exception __exception) { _updating = null; return __exception; } } [HarmonyPatch(typeof(WearNTear), "ClearCachedSupport")] private static class MeasureAgain { private static void Postfix(WearNTear __instance) { if (!_damage && !((Object)(object)__instance == (Object)(object)_updating) && Stability.TryGetInfo(__instance, out var info)) { info.GroundedDirty = true; if (!_digging || info.Grounded || info.Anchors.Exists((Anchor a) => a.Kind == HostKind.Bedrock)) { info.AnchorDirty = true; } } } } [HarmonyPatch(typeof(WearNTear), "RPC_HealthChanged")] private static class NotOnDamage { private static void Prefix() { _damage = true; } private static void Finalizer() { _damage = false; } } [HarmonyPatch(typeof(Heightmap), "Regenerate")] private static class OnlyGroundOnDigging { private static void Prefix() { _digging = true; } private static void Finalizer() { _digging = false; } } private const float SampleStep = 0.25f; private const int MaxPerAxis = 8; private const int MaxSamples = 400; private const int MaxMeasuresPerFrame = 12; private static readonly Collider[] Hits = (Collider[])(object)new Collider[256]; private static readonly List<Collider> RockHits = new List<Collider>(4); private static int _budgetFrame = -1; private static int _budgetUsed; private static readonly List<Vector3> Samples = new List<Vector3>(512); private static readonly List<Collider> Candidates = new List<Collider>(32); private static readonly List<WearNTear> CandidateHosts = new List<WearNTear>(32); private static readonly Dictionary<WearNTear, int> MasonryCount = new Dictionary<WearNTear, int>(); private static readonly List<bool> SampleInRock = new List<bool>(512); private static readonly List<WearNTear> SampleHost = new List<WearNTear>(512); private static readonly Dictionary<WearNTear, Collider> MasonryCollider = new Dictionary<WearNTear, Collider>(); private static int _mask = -1; private static int _terrainLayer = -1; private static WearNTear _updating; public static int MeasureCount; public static double MeasureSeconds; private static readonly List<float> Levels = new List<float>(512); private static readonly Dictionary<long, float> GroundCache = new Dictionary<long, float>(); private static int _groundMask = -1; private static bool _damage; private static bool _digging; private static int Mask { get { if (_mask == -1) { _mask = LayerMask.GetMask(new string[5] { "piece", "Default", "static_solid", "Default_small", "terrain" }); _terrainLayer = LayerMask.NameToLayer("terrain"); } return _mask; } } public static void Measure(WearNTear w, PieceInfo info) { long timestamp = Stopwatch.GetTimestamp(); try { MeasureNow(w, info); } finally { MeasureCount++; MeasureSeconds += (double)(Stopwatch.GetTimestamp() - timestamp) / (double)Stopwatch.Frequency; } } private static void MeasureNow(WearNTear w, PieceInfo info) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0266: 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_0279: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) info.AnchorDirty = false; info.MeasuredAt = Time.time; info.Anchors.Clear(); Samples.Clear(); SampleInRock.Clear(); SampleHost.Clear(); Candidates.Clear(); CandidateHosts.Clear(); MasonryCount.Clear(); MasonryCollider.Clear(); RockHits.Clear(); GroundCache.Clear(); Collider[] array = OwnColliders(w); bool flag = false; int mask = Mask; Collider[] array2 = array; foreach (Collider val in array2) { if (!Solid(val)) { continue; } AddSamples(val); Bounds bounds = val.bounds; flag = true; int num = Physics.OverlapBoxNonAlloc(((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).extents + Vector3.one * 0.05f, Hits, Quaternion.identity, mask); for (int j = 0; j < num; j++) { Collider val2 = Hits[j]; if (!((Object)(object)val2 == (Object)null) && Solid(val2) && ((Component)val2).gameObject.layer != _terrainLayer && Array.IndexOf(array, val2) < 0 && !Candidates.Contains(val2)) { WearNTear componentInParent = ((Component)val2).GetComponentInParent<WearNTear>(); if (!((Object)(object)componentInParent != (Object)null) || (!((Object)(object)componentInParent == (Object)(object)w) && componentInParent.m_supports && Cfg.IsHost(Stability.TierOf(componentInParent)))) { Candidates.Add(val2); CandidateHosts.Add(componentInParent); } } } } if (!flag || Samples.Count == 0) { return; } if (Samples.Count > 400) { float num2 = (float)Samples.Count / 400f; for (int k = 0; k < 400; k++) { Samples[k] = Samples[(int)((float)k * num2)]; } Samples.RemoveRange(400, Samples.Count - 400); } int num3 = 0; Collider val3 = null; string text = null; foreach (Vector3 sample in Samples) { bool flag2 = false; WearNTear val4 = null; if (GroundAt(sample, out var height) && sample.y < height - 0.02f) { flag2 = true; text = text ?? "ground"; } for (int l = 0; l < Candidates.Count; l++) { Collider val5 = Candidates[l]; if (!Inside(val5, sample)) { continue; } WearNTear val6 = CandidateHosts[l]; if ((Object)(object)val6 == (Object)null) { if (!RockHits.Contains(val5)) { RockHits.Add(val5); } if (!flag2) { flag2 = true; val3 = val3 ?? val5; text = ((text == null || text == "ground") ? "rock" : text); } } else if (!((Object)(object)val4 == (Object)(object)val6)) { MasonryCount.TryGetValue(val6, out var value); MasonryCount[val6] = value + 1; if (!MasonryCollider.ContainsKey(val6)) { MasonryCollider[val6] = val5; } val4 = val6; } } if (flag2) { num3++; } SampleInRock.Add(flag2); SampleHost.Add(val4); } float num4 = Samples.Count; if (num3 > 0) { Reach((int index) => SampleInRock[index], out var depth, out var length); info.Anchors.Add(new Anchor { Kind = HostKind.Bedrock, Share = (float)num3 / num4, Depth = depth, Length = length, Collider = val3, Rocks = RockHits.ToArray(), Name = text }); } foreach (KeyValuePair<WearNTear, int> item in MasonryCount) { float num5 = (float)item.Value / num4; if (Stability.TryGetInfo(item.Key, out var info2)) { int num6 = info2.Anchors.FindIndex((Anchor a) => (Object)(object)a.Host == (Object)(object)w); if (num6 >= 0) { if (info2.Anchors[num6].Share >= num5) { continue; } info2.Anchors.RemoveAt(num6); } } WearNTear key = item.Key; Reach((int index) => (Object)(object)SampleHost[index] == (Object)(object)key, out var depth2, out var length2); info.Anchors.Add(new Anchor { Kind = HostKind.Masonry, Share = num5, Depth = depth2, Length = length2, Host = item.Key, Collider = MasonryCollider[item.Key], Name = (((Component)item.Key).GetComponent<Piece>()?.m_name ?? Utils.GetPrefabName(((Component)item.Key).gameObject)) }); } } private static void Reach(Func<int, bool> inside, out float depth, out float length) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.zero; Vector3 val2 = Vector3.zero; int num = 0; int num2 = 0; for (int i = 0; i < Samples.Count; i++) { if (inside(i)) { val += Samples[i]; num++; } else { val2 += Samples[i]; num2++; } } if (num == 0) { depth = 0f; length = 0f; return; } val /= (float)num; Vector3 val3 = ((num2 > 0) ? (val - val2 / (float)num2) : Vector3.down); if (((Vector3)(ref val3)).sqrMagnitude < 1E-06f) { val3 = Vector3.down; } ((Vector3)(ref val3)).Normalize(); float num3 = float.MaxValue; float num4 = float.MinValue; float num5 = float.MaxValue; float num6 = float.MinValue; Levels.Clear(); for (int j = 0; j < Samples.Count; j++) { float num7 = Vector3.Dot(Samples[j], val3); Levels.Add(num7); num5 = Mathf.Min(num5, num7); num6 = Mathf.Max(num6, num7); if (inside(j)) { num3 = Mathf.Min(num3, num7); num4 = Mathf.Max(num4, num7); } } Levels.Sort(); float num8 = 0.25f; for (int k = 1; k < Levels.Count; k++) { float num9 = Levels[k] - Levels[k - 1]; if (num9 > 0.02f && num9 < num8) { num8 = num9; } } depth = num4 - num3 + num8; length = num6 - num5 + num8; } public static bool IsGrounded(WearNTear w) { //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_007e: 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) PieceInfo pieceInfo = Stability.Info(w); if (!pieceInfo.GroundedDirty && (!pieceInfo.Grounded || !Gone(pieceInfo.GroundedBy))) { return pieceInfo.Grounded; } pieceInfo.GroundedDirty = false; pieceInfo.Grounded = false; pieceInfo.GroundedBy = null; GroundCache.Clear(); Collider[] array = OwnColliders(w); int mask = Mask; Collider[] array2 = array; foreach (Collider val in array2) { if (!Solid(val)) { continue; } Bounds bounds = val.bounds; if (GroundAt(((Bounds)(ref bounds)).center, out var height) && ((Bounds)(ref bounds)).min.y < height - 0.02f) { pieceInfo.Grounded = true; return true; } int num = OverlapLikeTheGame(val, mask); for (int j = 0; j < num; j++) { Collider val2 = Hits[j]; if (!((Object)(object)val2 == (Object)null) && Solid(val2) && Array.IndexOf(array, val2) < 0 && (((Component)val2).gameObject.layer == _terrainLayer || (Object)(object)((Component)val2).GetComponentInParent<WearNTear>() == (Object)null)) { pieceInfo.Grounded = true; if (((Component)val2).gameObject.layer != _terrainLayer) { pieceInfo.GroundedBy = val2; } return true; } } } return false; } private static int OverlapLikeTheGame(Collider c, int mask) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0054: 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) BoxCollider val = (BoxCollider)(object)((c is BoxCollider) ? c : null); if (val != null) { Transform transform = ((Component)val).transform; Vector3 val2 = Vector3.Scale(transform.lossyScale, val.size) + Vector3.one * 0.3f; return Physics.OverlapBoxNonAlloc(transform.position + transform.TransformVector(val.center), val2 * 0.5f, Hits, transform.rotation, mask); } Bounds bounds = c.bounds; return Physics.OverlapBoxNonAlloc(((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).extents + Vector3.one * 0.15f, Hits, Quaternion.identity, mask); } private static Collider[] OwnColliders(WearNTear w) { if (w.m_colliders == null) { w.SetupColliders(); } return w.m_colliders ?? Array.Empty<Collider>(); } private static bool GroundAt(Vector3 p, out float height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_009c: Unknown result type (might be due to invalid IL or missing references) long key = ((long)Mathf.RoundToInt(p.x * 20f) << 32) ^ (uint)Mathf.RoundToInt(p.z * 20f); if (GroundCache.TryGetValue(key, out height)) { return !float.IsNaN(height); } if (_groundMask == -1) { _groundMask = LayerMask.GetMask(new string[1] { "terrain" }); } RaycastHit val = default(RaycastHit); bool flag = Physics.Raycast(new Vector3(p.x, 6000f, p.z), Vector3.down, ref val, 10000f, _groundMask); height = (flag ? ((RaycastHit)(ref val)).point.y : float.NaN); GroundCache[key] = height; return flag; } private static bool Solid(Collider c) { if ((Object)(object)c != (Object)null && c.enabled && ((Component)c).gameObject.activeInHierarchy && !c.isTrigger) { return (Object)(object)c.attachedRigidbody == (Object)null; } return false; } private static bool Gone(Collider c) { if (c != null) { if (Object.op_Implicit((Object)(object)c) && c.enabled) { return !((Component)c).gameObject.activeInHierarchy; } return true; } return false; } private static void AddSamples(Collider c) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_00af: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: 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) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) BoxCollider val = (BoxCollider)(object)((c is BoxCollider) ? c : null); if (val != null) { Transform transform = ((Component)val).transform; Vector3 val2 = Vector3.Scale(transform.lossyScale, val.size); int num = Count(val2.x); int num2 = Count(val2.y); int num3 = Count(val2.z); Vector3 val3 = default(Vector3); for (int i = 0; i < num; i++) { for (int j = 0; j < num2; j++) { for (int k = 0; k < num3; k++) { ((Vector3)(ref val3))..ctor(((float)i + 0.5f) / (float)num - 0.5f, ((float)j + 0.5f) / (float)num2 - 0.5f, ((float)k + 0.5f) / (float)num3 - 0.5f); Samples.Add(transform.TransformPoint(val.center + Vector3.Scale(val3, val.size))); } } } return; } Bounds bounds = c.bounds; int num4 = Count(((Bounds)(ref bounds)).size.x); int num5 = Count(((Bounds)(ref bounds)).size.y); int num6 = Count(((Bounds)(ref bounds)).size.z); MeshCollider val4 = (MeshCollider)(object)((c is MeshCollider) ? c : null); bool flag = val4 == null || val4.convex; for (int l = 0; l < num4; l++) { for (int m = 0; m < num5; m++) { for (int n = 0; n < num6; n++) { Vector3 val5 = ((Bounds)(ref bounds)).min + Vector3.Scale(((Bounds)(ref bounds)).size, new Vector3(((float)l + 0.5f) / (float)num4, ((float)m + 0.5f) / (float)num5, ((float)n + 0.5f) / (float)num6)); bool num7; if (!flag) { num7 = Inside(c, val5); } else { Vector3 val6 = c.ClosestPoint(val5) - val5; num7 = ((Vector3)(ref val6)).sqrMagnitude < 0.0001f; } if (num7) { Samples.Add(val5); } } } } } private static int Count(float size) { return Mathf.Clamp(Mathf.RoundToInt(size / 0.25f), 1, 8); } private static bool Inside(Collider c, Vector3 p) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0024: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0043: 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_004b: 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_006a: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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) MeshCollider val = (MeshCollider)(object)((c is MeshCollider) ? c : null); Bounds bounds; Vector3 val2; if (val == null || val.convex) { bounds = c.bounds; if (((Bounds)(ref bounds)).Contains(p)) { val2 = c.ClosestPoint(p) - p; return ((Vector3)(ref val2)).sqrMagnitude < 0.0001f; } return false; } bounds = c.bounds; if (!((Bounds)(ref bounds)).Contains(p)) { return false; } bool queriesHitBackfaces = Physics.queriesHitBackfaces; Physics.queriesHitBackfaces = true; try { bounds = c.bounds; val2 = ((Bounds)(ref bounds)).size; float num = ((Vector3)(ref val2)).magnitude + 1f; RaycastHit val3 = default(RaycastHit); RaycastHit val4 = default(RaycastHit); return c.Raycast(new Ray(p, Vector3.up), ref val3, num) && ((RaycastHit)(ref val3)).normal.y > 0f && c.Raycast(new Ray(p, Vector3.down), ref val4, num) && ((RaycastHit)(ref val4)).normal.y < 0f; } finally { Physics.queriesHitBackfaces = queriesHitBackfaces; } } public static float Hold(WearNTear w, PieceInfo info, float ownMax, out Anchor best, bool anyTime = false) { best = default(Anchor); if (!Cfg.Anchoring.Value) { return 0f; } bool flag = Lost(info); if (info.AnchorDirty || flag) { if (anyTime || Budget()) { Measure(w, info); } else if (flag) { info.Anchors.Clear(); } } float num = 0f; float value = Cfg.FullHoldShare.Value; float value2 = Cfg.MinDepth.Value; foreach (Anchor anchor in info.Anchors) { HostKind hostKind = anchor.Kind; float num2 = 0f; if ((Object)(object)anchor.Host != (Object)null) { if ((Object)(object)anchor.Host.m_nview == (Object)null || !anchor.Host.m_nview.IsValid()) { continue; } num2 = anchor.Host.GetSupport(); hostKind = ((!IsGrounded(anchor.Host)) ? HostKind.Masonry : HostKind.Foundation); if (hostKind == HostKind.Masonry && Stability.TryGetInfo(anchor.Host, out var info2) && info2.AnchorSupport > 0f && num2 <= info2.AnchorSupport + 0.5f) { continue; } } float quality = AnchorRule.Quality(anchor.Share, anchor.Depth, value, value2); float num3 = AnchorRule.Support(hostKind, quality, ownMax, num2); if (num3 > 0f && AnchorRule.Better(hostKind, num3, best.Kind, num)) { best = anchor; best.Kind = hostKind; num = num3; } } info.AnchorSupport = num; return num; } private static bool Lost(PieceInfo info) { foreach (Anchor anchor in info.Anchors) { if (Gone(anchor.Collider) || (anchor.Host != null && (!Object.op_Implicit((Object)(object)anchor.Host) || !anchor.Host.m_nview.IsValid()))) { return true; } if (anchor.Rocks == null) { continue; } Collider[] rocks = anchor.Rocks; for (int i = 0; i < rocks.Length; i++) { if (Gone(rocks[i])) { return true; } } } return false; } private static bool Budget() { if (_budgetFrame != Time.frameCount) { _budgetFrame = Time.frameCount; _budgetUsed = 0; } if (_budgetUsed >= 12) { return false; } _budgetUsed++; return true; } private static void SetSupport(WearNTear w, float support) { if (!w.m_support.Equals(support)) { w.m_support = support; ZDO zDO = w.m_nview.GetZDO(); if (zDO != null) { zDO.Set(ZDOVars.s_support, support); } } } } public sealed class PieceInfo { public Tier Tier; public bool TierKnown; public bool AnchorDirty = true; public float MeasuredAt; public float LastHold = -1000f; public readonly List<Anchor> Anchors = new List<Anchor>(2); public float AnchorSupport; public bool Grounded; public bool GroundedDirty = true; public Collider GroundedBy; } public static class Stability { [HarmonyPatch(typeof(WearNTear), "GetMaterialProperties")] private static class TierValuesForPiece { private static bool Prefix(WearNTear __instance, ref float maxSupport, ref float minSupport, ref float horizontalLoss, ref float verticalLoss) { if ((Object)(object)__instance == (Object)null) { return true; } TierValues tierValues = ValuesOf(__instance); maxSupport = tierValues.Max; minSupport = tierValues.Min; verticalLoss = tierValues.Up; horizontalLoss = tierValues.Out; return false; } } [HarmonyPatch(typeof(WearNTear), "OnDestroy")] private static class ForgetPiece { private static void Postfix(WearNTear __instance) { Infos.Remove(((Object)__instance).GetInstanceID()); } } [HarmonyPatch(typeof(Game), "Start")] private static class FreshWorld { private static void Postfix() { TierByPrefab.Clear(); Infos.Clear(); } } private static readonly Dictionary<int, PieceInfo> Infos; private static readonly Dictionary<string, Tier> TierByPrefab; public static bool Bypass; static Stability() { Infos = new Dictionary<int, PieceInfo>(); TierByPrefab = new Dictionary<string, Tier>(); Cfg.Changed += OnConfigChanged; SynchronizationManager.OnConfigurationSynchronized += delegate { OnConfigChanged(); }; } public static PieceInfo Info(WearNTear w) { int instanceID = ((Object)w).GetInstanceID(); if (!Infos.TryGetValue(instanceID, out var value)) { value = new PieceInfo(); Infos[instanceID] = value; } return value; } public static bool TryGetInfo(WearNTear w, out PieceInfo info) { return Infos.TryGetValue(((Object)w).GetInstanceID(), out info); } public static Tier TierOf(WearNTear w) { PieceInfo pieceInfo = Info(w); if (!pieceInfo.TierKnown) { pieceInfo.Tier = ResolveTier(w); pieceInfo.TierKnown = true; } return pieceInfo.Tier; } public static TierValues ValuesOf(WearNTear w) { Tier tier = TierOf(w); if (Bypass || !Cfg.UseTiers.Value) { return Strength.Vanilla((tier == Tier.Flametal) ? Tier.Iron : tier); } return Cfg.Values(tier); } private static Tier ResolveTier(WearNTear w) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected I4, but got Unknown string prefabName = Utils.GetPrefabName(((Component)w).gameObject); if (Cfg.Overrides.TryGetValue(prefabName, out var value)) { return value; } if (TierByPrefab.TryGetValue(prefabName, out var value2)) { return value2; } Tier tier = (Tier)w.m_materialType; if (!Enum.IsDefined(typeof(Tier), tier)) { tier = Tier.Wood; } Piece component = ((Component)w).GetComponent<Piece>(); if ((Object)(object)component != (Object)null && component.m_resources != null && tier == Tier.Iron) { Requirement[] resources = component.m_resources; foreach (Requirement val in resources) { if ((((Object)(object)val?.m_resItem != (Object)null) ? ((Object)((Component)val.m_resItem).gameObject).name : "").StartsWith("Flametal", StringComparison.OrdinalIgnoreCase)) { tier = Tier.Flametal; break; } } } TierByPrefab[prefabName] = tier; return tier; } public static void OnConfigChanged() { TierByPrefab.Clear(); foreach (PieceInfo value in Infos.Values) { value.TierKnown = false; value.AnchorDirty = true; value.GroundedDirty = true; } foreach (WearNTear allInstance in WearNTear.GetAllInstances()) { if ((Object)(object)allInstance != (Object)null) { allInstance.ClearCachedSupport(); } } } } public static class SupportHover { [HarmonyPatch(typeof(Hud), "UpdateCrosshair")] private static class ShowUnderCrosshair { private static void Postfix(Hud __instance, Player player) { try { if (_broken || !Cfg.ShowSupport.Value || (Object)(object)player == (Object)null || (Object)(object)__instance.m_hoverName == (Object)null) { return; } Piece hoveringPiece = player.GetHoveringPiece(); if ((Object)(object)hoveringPiece == (Object)null) { return; } WearNTear component = ((Component)hoveringPiece).GetComponent<WearNTear>(); if (!((Object)(object)component == (Object)null)) { string text = Describe(component); if (!string.IsNullOrEmpty(text)) { string text2 = ((TMP_Text)__instance.m_hoverName).text; ((TMP_Text)__instance.m_hoverName).text = (string.IsNullOrEmpty(text2) ? text : (text2 + "\n" + text)); } } } catch (Exception ex) { BetterStabilityPlugin.Log.LogWarning((object)("Support hover failed, switched off until restart: " + ex)); _broken = true; } } } private static bool _broken; private static WearNTear _piece; private static PieceInfo _info = new PieceInfo(); public static string Describe(WearNTear w) { if ((Object)(object)w == (Object)null || (Object)(object)w.m_nview == (Object)null || !w.m_nview.IsValid()) { return ""; } Tier t = Stability.TierOf(w); TierValues tierValues = Stability.ValuesOf(w); float support = w.GetSupport(); string text = ((support >= tierValues.Max - 0.001f) ? Lang.T("bs_hover_support_full", Lang.TierName(t)) : Lang.T("bs_hover_support", Lang.TierName(t), Mathf.FloorToInt(Mathf.Clamp01(support / tierValues.Max) * 100f))); if (!Cfg.Anchoring.Value) { return text; } if (_piece != w || Time.time - _info.MeasuredAt > 1f) { _piece = w; _info = new PieceInfo(); } PieceInfo info = _info; Anchors.Hold(w, info, tierValues.Max, out var best, anyTime: true); if (best.Kind == HostKind.None) { Anchor a = default(Anchor); foreach (Anchor anchor in info.Anchors) { if (anchor.Kind != HostKind.Bedrock && anchor.Depth > a.Depth) { a = anchor; } } if (a.Depth >= 0.05f) { return text + "\n<color=#c0c0c0>" + Lang.T("bs_hover_shallow", Metres(a.Depth), HostName(a)) + "</color>"; } return text; } float num = AnchorRule.Quality(best.Share, best.Depth, Cfg.FullHoldShare.Value, Cfg.MinDepth.Value); if (num < 0.999f) { if (best.Kind == HostKind.Bedrock) { return text; } float m = best.Length * Cfg.FullHoldShare.Value; return text + "\n<color=#ffd98a>" + Lang.T("bs_hover_set", Metres(best.Depth), HostName(best)) + "</color>\n<color=#c0c0c0>" + Lang.T("bs_hover_partial", Mathf.RoundToInt(num * 100f), Metres(m)) + "</color>"; } return text + "\n<color=#ffd98a>" + best.Kind switch { HostKind.Bedrock => Lang.T("bs_hover_bedrock", Metres(best.Depth), HostName(best)), HostKind.Foundation => Lang.T("bs_hover_foundation", Metres(best.Depth)), _ => Lang.T("bs_hover_masonry", Metres(best.Depth), HostName(best)), } + "</color>"; } private static string HostName(Anchor a) { if (a.Kind == HostKind.Bedrock) { return Lang.T((a.Name == "ground") ? "bs_host_ground" : "bs_host_rock"); } if (a.Kind == HostKind.Foundation) { return Lang.T("bs_host_foundation"); } return Lang.L(a.Name ?? ""); } public static string Metres(float m) { bool num = Localization.instance != null && Localization.instance.GetSelectedLanguage() == "German"; string text = m.ToString("0.0", CultureInfo.InvariantCulture); if (!num) { return text; } return text.Replace('.', ','); } } } namespace BetterStability.Dev { public class SelfTest : MonoBehaviour { private sealed class Row { public Tier Tier; public string Prefab; public Func<int, WearNTear> Place; public Func<int, bool> Clear = (int _) => true; public readonly List<WearNTear> Pieces = new List<WearNTear>(); public bool Done; public bool Capped; public int Standing; public int Expected; } private string _out; private readonly List<string> _report = new List<string>(); private static readonly (Tier tier, string prefab)[] Columns = new(Tier, string)[8] { (Tier.Wood, "wood_pole2"), (Tier.CoreWood, "wood_pole_log"), (Tier.Timberwood, "stave_pole_2m"), (Tier.Stone, "stone_pillar"), (Tier.Iron, "woodiron_pole"), (Tier.BlackMarble, "blackmarble_2x2x2"), (Tier.Grausten, "Piece_grausten_wall_2x2"), (Tier.Flametal, "Piece_flametal_pillar") }; private static readonly (Tier tier, string prefab)[] Beams = new(Tier, string)[8] { (Tier.Wood, "wood_beam"), (Tier.CoreWood, "wood_wall_log"), (Tier.Timberwood, "stave_beam_2m"), (Tier.Stone, "stone_floor_2x2"), (Tier.Iron, "woodiron_beam"), (Tier.BlackMarble, "blackmarble_floor"), (Tier.Grausten, "Piece_grausten_floor_2x2"), (Tier.Flametal, "Piece_flametal_beam") }; private int _pass; private int _fail; private readonly List<Vector3> _taken = new List<Vector3>(); private WearNTear Place(string prefab, Vector3 pos, Quaternion rot) { //IL_0029: 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) GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab); if ((Object)(object)prefab2 == (Object)null) { Note("place: missing prefab " + prefab); return null; } WearNTear component = Object.Instantiate<GameObject>(prefab2, pos, rot).GetComponent<WearNTear>(); if ((Object)(object)component != (Object)null) { component.OnPlaced(); } return component; } private static bool Alive(WearNTear w) { if ((Object)(object)w != (Object)null && (Object)(object)w.m_nview != (Object)null) { return w.m_nview.IsValid(); } return false; } private static float SupportOf(WearNTear w) { if (!Alive(w)) { return 0f; } return w.GetSupport(); } private static float Percent(WearNTear w) { if (!Alive(w)) { return 0f; } return 100f * w.GetSupport() / Stability.ValuesOf(w).Max; } private IEnumerator Settle(IList<WearNTear> pieces, float quiet = 4f, float limit = 120f) { float end = Time.realtimeSinceStartup + limit; float stableSince = Time.realtimeSinceStartup; string last = ""; while (Time.realtimeSinceStartup < end) { yield return (object)new WaitForSecondsRealtime(0.5f); string text = string.Join(",", pieces.Select((WearNTear p) => (!Alive(p)) ? "x" : Mathf.RoundToInt(p.GetSupport()).ToString())); if (text != last) { last = text; stableSince = Time.realtimeSinceStartup; } else if (Time.realtimeSinceStartup - stableSince >= quiet) { yield break; } } Note("settle: still changing after " + limit + " s"); } private static void Clear(IEnumerable<WearNTear> pieces) { foreach (WearNTear piece in pieces) { if (Alive(piece)) { piece.m_nview.Destroy(); } } } private static List<WearNTear> PiecesNear(Vector3 c, float r) { //IL_0007: 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) return (from w in WearNTear.GetAllInstances() where Alive(w) && Vector3.Distance(((Component)w).transform.position, c) < r select w).ToList(); } private static Bounds SolidBounds(GameObject go) { //IL_0002: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Bounds result = default(Bounds); bool flag = false; Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { if (!val.isTrigger) { if (!flag) { result = val.bounds; flag = true; } else { ((Bounds)(ref result)).Encapsulate(val.bounds); } } } return result; } public static void TryStart(GameObject host) { string environmentVariable = Environment.GetEnvironmentVariable("STABILITY_SELFTEST"); if (string.IsNullOrEmpty(environmentVariable)) { return; } Application.runInBackground = true; string[] commandLineArgs = Environment.GetCommandLineArgs(); bool flag = false; for (int i = 0; i < commandLineArgs.Length - 1; i++) { if (commandLineArgs[i] == "-savedir" && !string.IsNullOrEmpty(commandLineArgs[i + 1])) { Directory.CreateDirectory(commandLineArgs[i + 1]); Utils.SetSaveDataPath(commandLineArgs[i + 1]); flag = true; } } if (!flag) { BetterStabilityPlugin.Log.LogError((object)"Self test needs -savedir <scratch folder>; not starting it."); } else { host.AddComponent<SelfTest>()._out = environmentVariable; } } private static bool Flag(string name) { return (Environment.GetEnvironmentVariable("STABILITY_SELFTEST_" + name) ?? "") == "1"; } private void Start() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown Directory.CreateDirectory(_out); Application.logMessageReceived += new LogCallback(OnLog); ((MonoBehaviour)this).StartCoroutine(Run()); } private void OnLog(string condition, string stackTrace, LogType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) if ((int)type == 4 || (int)type == 0) { _report.Add($"[{type}] {condition}\n{stackTrace}"); } } private IEnumerator Run() { float timeout = Time.realtimeSinceStartup + 600f; while (((Object)(object)FejdStartup.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) && Time.realtimeSinceStartup < timeout) { yield return null; } yield return (object)new WaitForSecondsRealtime(4f); if (Flag("WORLD")) { yield return ((MonoBehaviour)this).StartCoroutine(Safe(EnterWorld())); if ((Object)(object)Player.m_localPlayer != (Object)null) { if (Flag("RESEARCH")) { yield return ((MonoBehaviour)this).StartCoroutine(Safe(Research())); } bool all = Flag("ALL"); if (Flag("DE")) { Localization.instance.SetLanguage("German"); } Player.m_localPlayer.SetGodMode(true); Player.m_localPlayer.SetGhostMode(true); EnvMan.instance.m_debugTimeOfDay = true; EnvMan.instance.m_debugTime = 0.38f; if (all || Flag("ANCHOR")) { yield return ((MonoBehaviour)this).StartCoroutine(Safe(Anchoring())); } if (all || Flag("FLOAT")) { yield return ((MonoBehaviour)this).StartCoroutine(Safe(NoFloating())); } if (all || Flag("TOWERS")) { yield return ((MonoBehaviour)this).StartCoroutine(Safe(Towers())); } if (all || Flag("OVERHANG")) { yield return ((MonoBehaviour)this).StartCoroutine(Safe(Overhang())); } if (all || Flag("CLIFF")) { yield return ((MonoBehaviour)this).StartCoroutine(Safe(Cliff())); } _report.Add($"anchor cost: {Anchors.MeasureCount} measurings, {Anchors.MeasureSeconds * 1000.0:0.0} ms in all " + $"({((Anchors.MeasureCount > 0) ? (Anchors.MeasureSeconds * 1000000.0 / (double)Anchors.MeasureCount) : 0.0):0} µs each)"); } } _report.Add($"RESULT pass {_pass} fail {_fail}"); Flush(); Application.logMessageReceived -= new LogCallback(OnLog); yield return (object)new WaitForSecondsRealtime(0.5f); Application.Quit(); } private IEnumerator Safe(IEnumerator inner) { while (true) { object current; try { if (!inner.MoveNext()) { break; } current = inner.Current; } catch (Exception ex) { _report.Add("step failed: " + ex); _fail++; break; } yield return current; } } private void Flush() { File.WriteAllLines(Path.Combine(_out, "report.txt"), _report); } private IEnumerator Research() { ZNetScene instance = ZNetScene.instance; List<GameObject> list = (from p in instance.m_prefabs.Concat(instance.m_namedPrefabs.Values) where (Object)(object)p != (Object)null group p by ((Object)p).name into g select g.First() into p orderby ((Object)p).name select p).ToList(); _report.Add("research: prefabs " + list.Count); HashSet<string> hammerPieces = new HashSet<string>(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); PieceTable val = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces); if ((Object)(object)val != (Object)null) { foreach (GameObject item in val.m_pieces.Where((GameObject p) => (Object)(object)p != (Object)null)) { hammerPieces.Add(((Object)item).name); Piece component = item.GetComponent<Piece>(); WearNTear component2 = item.GetComponent<WearNTear>(); string text = ((component?.m_resources == null) ? "" : string.Join(" ", from r in component.m_resources where (Object)(object)r?.m_resItem != (Object)null select ((Object)r.m_resItem).name + ":" + r.m_amount)); string text2 = string.Join(" ", (from c in item.GetComponentsInChildren<Collider>(true) where !c.isTrigger select c).Select(Shape)); List<string> report = _report; string[] array = new string[6]; object[] obj = new object[4] { ((Object)item).name, Lang.L(component?.m_name), component?.m_category, null }; object obj2; if (component == null) { obj2 = null; } else { CraftingStation craftingStation = component.m_craftingStation; obj2 = ((craftingStation != null) ? ((Object)craftingStation).name : null); } obj[3] = obj2; array[0] = string.Format("HAMMER {0} '{1}' cat {2} station {3} ", obj); array[1] = (((Object)(object)component2 == (Object)null) ? "noWNT" : $"mat {component2.m_materialType} hp {component2.m_health} supports {component2.m_supports} com {component2.m_comOffset} "); array[2] = "| "; array[3] = text; array[4] = " | "; array[5] = text2; report.Add(string.Concat(array)); } } _report.Add("hammer pieces: " + hammerPieces.Count); foreach (IGrouping<MaterialType, GameObject> item2 in from p in list where !hammerPieces.Contains(((Object)p).name) && (Object)(object)p.GetComponent<WearNTear>() != (Object)null group p by p.GetComponent<WearNTear>().m_materialType) { _report.Add($"WORLD {item2.Key} ({item2.Count()}): " + string.Join(" ", item2.Select((GameObject p) => ((Object)p).name).Take(60))); } if ((Object)(object)ZoneSystem.instance != (Object)null) { foreach (ZoneLocation item3 in ZoneSystem.instance.m_locations.Where((ZoneLocation l) => l != null && l.m_enable && l.m_prefabName != null && (l.m_prefabName.IndexOf("tower", StringComparison.OrdinalIgnoreCase) >= 0 || l.m_prefabName.IndexOf("seal", StringComparison.OrdinalIgnoreCase) >= 0))) { _report.Add($"LOCATION {item3.m_prefabName} biome {item3.m_biome} quantity {item3.m_quantity}"); } } foreach (GameObject item4 in ObjectDB.instance.m_items.Where((GameObject i) => (Object)(object)i != (Object)null && (((Object)i).name.IndexOf("Flametal", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)i).name.IndexOf("Marble", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)i).name.IndexOf("Grausten", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)i).name.IndexOf("Iron", StringComparison.OrdinalIgnoreCase) >= 0))) { _report.Add("ITEM " + ((Object)item4).name + " '" + Lang.L(item4.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name) + "'"); } Vector3 position = ((Component)Player.m_localPlayer).transform.position; WorldGenerator instance2 = WorldGenerator.instance; int num = 0; for (float num2 = 50f; num2 < 3000f; num2 += 25f) { if (num >= 25) { break; } for (int num3 = 0; num3 < 48; num3++) { if (num >= 25) { break; } float num4 = (float)num3 * (float)Math.PI * 2f / 48f; float num5 = position.x + Mathf.Cos(num4) * num2; float num6 = position.z + Mathf.Sin(num4) * num2; float height = instance2.GetHeight(num5, num6); if (height < ZoneSystem.instance.m_waterLevel + 3f) { continue; } float num7 = 0f; float num8 = 0f; for (int num9 = 0; num9 < 8; num9++) { float num10 = (float)num9 * (float)Math.PI / 4f; float height2 = instance2.GetHeight(num5 + Mathf.Cos(num10) * 3f, num6 + Mathf.Sin(num10) * 3f); if ((height2 - height) / 3f > num7) { num7 = (height2 - height) / 3f; num8 = num10 * 57.29578f; } } if (!(num7 < 1.6f)) { num++; _report.Add($"STEEP ({num5:0},{height:0.0},{num6:0}) slope {num7:0.00} uphill-dir {num8:0} biome {instance2.GetBiome(num5, num6, 0.02f, false)} dist {num2:0}"); } } } Flush(); yield break; } private static string Shape(Collider c) { //IL_001f: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) //IL_005a: 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_006f: 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_00c5: 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_00a1: Unknown result type (might be due to invalid IL or missing references) BoxCollider val = (BoxCollider)(object)((c is BoxCollider) ? c : null); if (val == null) { MeshCollider val2 = (MeshCollider)(object)((c is MeshCollider) ? c : null); Bounds bounds; if (val2 != null) { string obj = (val2.convex ? "C" : ""); bounds = ((Collider)val2).bounds; return "mesh" + obj + Fmt(((Bounds)(ref bounds)).size); } string name = ((object)c).GetType().Name; bounds = c.bounds; return name + Fmt(((Bounds)(ref bounds)).size); } return "box" + Fmt(Vector3.Scale(val.size, ((Component)val).transform.lossyScale)) + "@" + Fmt(((Component)val).transform.position + ((Component)val).transform.TransformVector(val.center) - ((Component)val).transform.root.position); } private static string Fmt(Vector3 v) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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) return $"({v.x:0.##},{v.y:0.##},{v.z:0.##})"; } private IEnumerator EnterWorld() { FejdStartup instance = FejdStartup.instance; if ((Object)(object)instance == (Object)null) { _report.Add("world: no FejdStartup"); yield break; } PlayerProfile val = new PlayerProfile("stabilitytester", (FileSource)2); val.SetName("Mason"); val.m_firstSpawn = false; val.Save(); Game.SetProfile("stabilitytester", (FileSource)2); World val2 = new World("StabilityTest", "HQ7GnTiegh") { m_fileSource = (FileSource)2 }; val2.SaveWorldFWLData(DateTime.Now); instance.m_world = val2; instance.m_startingWorld = true; ZNet.SetServer(true, false, false, val2.m_name, "", val2); ZNet.ResetServerHost(); instance.TransitionToMainScene(); _report.Add("world: loading " + val2.m_name + " seed " + val2.m_seedName); float timeout = Time.realtimeSinceStartup + 300f; while (((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ((Character)Player.m_localPlayer).IsTeleporting()) && Time.realtimeSinceStartup < timeout) { yield return null; } if ((Object)(object)Player.m_localPlayer == (Object)null) { _report.Add("world: player never spawned"); yield break; } yield return (object)new WaitForSecondsRealtime(6f); _report.Add("world: spawned at " + ((object)((Component)Player.m_localPlayer).transform.position/*cast due to .constrained prefix*/).ToString()); } private void UseVanilla(bool vanilla) { Stability.Bypass = vanilla; Stability.OnConfigChanged(); } private TierValues ValuesFor(Tier t, bool vanilla) { if (!vanilla) { return Cfg.Values(t); } return Strength.Vanilla((t == Tier.Flametal) ? Tier.Iron : t); } private IEnumerator Grow(List<Row> rows, int maxPieces) { float limit = Time.realtimeSinceStartup + 900f; for (int k = 0; k < maxPieces; k++) { if (!(Time.realtimeSinceStartup < limit)) { break; } List<(Row row, WearNTear w)> added = new List<(Row, WearNTear)>(); foreach (Row row2 in rows) { if (row2.Done) { continue; } if (!row2.Clear(k)) { row2.Done = true; row2.Capped = true; continue; } WearNTear val = row2.Place(k); if ((Object)(object)val == (Object)null) { row2.Done = true; continue; } row2.Pieces.Add(val); added.Add((row2, val)); } if (added.Count == 0) { break; } float until = Time.realtimeSinceStartup + 6f; while (Time.realtimeSinceStartup < until && added.Any(((Row row, WearNTear w) a) => Alive(a.w) && !Checked(a.w))) { yield return null; } yield return (object)new WaitForSecondsRealtime(0.4f); foreach (var item in added) { var (row, _) = item; if (!Alive(item.w) || row.Pieces.Take(row.Pieces.Count - 1).Any((WearNTear x) => !Alive(x))) { row.Done = true; } } } yield return ((MonoBehaviour)this).StartCoroutine(Settle(rows.SelectMany((Row r) => r.Pieces).ToList(), 6f, 240f)); foreach (Row row3 in rows) { row3.Standing = row3.Pieces.TakeWhile(Alive).Count(); if (!row3.Done) { row3.Capped = true; } } } private static bool Checked(WearNTear w) { if (w.m_supportColliders.Count <= 0 && !(w.m_support < Stability.ValuesOf(w).Max - 0.01f)) { if (Stability.TryGetInfo(w, out var info)) { return !info.AnchorDirty; } return false; } return true; } private IEnumerator Towers() { Player localPlayer = Player.m_localPlayer; Vector3 site = FlatSpot(((Component)localPlayer).transform.position); ClearArea(site + new Vector3(17f, 0f, -30f), 95f); Stand(localPlayer, site + new Vector3(-12f, 0f, -14f)); yield return (object)new WaitForSecondsRealtime(2f); Dictionary<(Tier, bool), float> heights = new Dictionary<(Tier, bool), float>(); bool[] array = new bool[2] { true, false }; foreach (bool vanilla in array) { UseVanilla(vanilla); List<Row> rows = new List<Row>(); Dictionary<Row, float> steps = new Dictionary<Row, float>(); for (int j = 0; j < Columns.Length; j++) { (Tier, string) tuple = Columns[j]; Tier item = tuple.Item1; string prefab = tuple.Item2; Bounds shape = LocalShape(prefab); float step = ((Bounds)(ref shape)).size.y; Vector3 at = Ground(site + new Vector3((float)j * 5f, 0f, 0f)); Row row = new Row { Tier = item, Prefab = prefab, Expected = Mathf.RoundToInt(Strength.ColumnHeight(ValuesFor(item, vanilla), step) / step), Place = (int k) => Place(prefab, at + Vector3.up * ((float)k * step - ((Bounds)(ref shape)).min.y - 0.2f), Quaternion.identity) }; rows.Add(row); steps[row] = step; } yield return ((MonoBehaviour)this).StartCoroutine(Grow(rows, 130)); string text = (vanilla ? "vanilla" : "mod"); foreach (Row item3 in rows) { float num = (float)item3.Standing * steps[item3]; heights[(item3.Tier, vanilla)] = num; Note(string.Format("tower {0} {1} ({2}): {3} pieces = {4}{5:0.#} m (formula {6})", text, item3.Tier, item3.Prefab, item3.Standing, item3.Capped ? "≥ " : "", num, item3.Expected)); Check(Mathf.Abs(item3.Standing - item3.Expected) <= 1 || (item3.Capped && item3.Standing >= item3.Expected), $"tower {text} {item3.Tier}: {item3.Standing} pieces stand, the formula says {item3.Expected} (±1)"); } if (!vanilla) { Vector3 val = Ground(site + new Vector3(17.5f, 0f, 0f)); yield return ((MonoBehaviour)this).StartCoroutine(MainShot("01_towers", Above(val + Vector3.up * 60f, 180f, 135f, 22f), 60f)); } Clear(rows.SelectMany((Row r) => r.Pieces)); yield return (object)new WaitForSecondsRealtime(2f); } (Tier, string)[] columns = Columns; for (int num2 = 0; num2 < columns.Length; num2++) { Tier item2 = columns[num2].Item1; Check(heights[(item2, false)] >= heights[(item2, true)], $"tower {item2}: mod {heights[(item2, false)]:0} m ≥ vanilla {heights[(item2, true)]:0} m"); } Check(heights[(Tier.BlackMarble, false)] >= 3f * heights[(Tier.BlackMarble, true)], "black marble builds at least 3 × as high as in vanilla"); Check(heights[(Tier.BlackMarble, false)] > heights[(Tier.Iron, false)], "black marble builds higher than iron"); Check(heights[(Tier.Grausten, false)] > heights[(Tier.BlackMarble, false)], "grausten builds higher than black marble"); Check(heights[(Tier.Flametal, false)] > heights[(Tier.Grausten, false)], "flametal builds higher than grausten"); Check(heights[(Tier.Flametal, false)] >= 2f * heights[(Tier.Iron, false)], "flametal builds at least twice as high as iron"); UseVanilla(vanilla: false); } private IEnumerator Overhang() { Player localPlayer = Player.m_localPlayer; Vector3 origin = FlatSpot(((Component)localPlayer).transform.position) + new Vector3(-20f, 0f, -20f); ClearArea(origin + new Vector3(40f, 0f, 21f), 100f); Stand(localPlayer, origin + new Vector3(-8f, 0f, -6f)); Dictionary<(Tier, bool), int> reach = new Dictionary<(Tier, bool), int>(); Dictionary<(Tier, bool), bool> capped = new Dictionary<(Tier, bool), bool>(); bool[] array = new bool[2] { true, false }; foreach (bool vanilla in array) { UseVanilla(vanilla); ClearArea(origin + new Vector3(40f, 0f, 21f), 100f); yield return (object)new WaitForSecondsRealtime(1f); List<GameObject> plinths = new List<GameObject>(); List<Row> rows = new List<Row>(); for (int j = 0; j < Beams.Length; j++) { (Tier, string) tuple = Beams[j]; Tier tier = tuple.Item1; string prefab = tuple.Item2; Vector3 foot = Ground(origin + new Vector3(0f, 0f, (float)j * 6f)); float num = foot.y + 25f; plinths.Add(Plinth(new Vector3(foot.x, num - 13.5f, foot.z), new Vector3(2f, 27f, 2f))); float y = num - 1.5f; Bounds world; Row item = new Row { Tier = tier, Prefab = prefab, Expected = 1 + Mathf.RoundToInt(Strength.Overhang(ValuesFor(tier, vanilla)) / 2f), Place = (int k) => PlaceAligned(prefab, new Vector3(foot.x + 1f - 0.05f + 1f + (float)k * 2f, y, foot.z), Vector3.right, out world), Clear = (int k) => RowClear(new Vector3(foot.x + 2f + (float)k * 2f, y, foot.z), tier, k) }; rows.Add(item); } yield return ((MonoBehaviour)this).StartCoroutine(Grow(rows, 80)); string text = (vanilla ? "vanilla" : "mod"); foreach (Row item3 in rows) { reach[(item3.Tier, vanilla)] = item3.Standing; if (!vanilla && (item3.Tier == Tier.Grausten || item3.Tier == Tier.BlackMarble)) { int[] array2 = new int[4] { 0, 1, 3, item3.Pieces.Count - 1 }; foreach (int num3 in array2) { if (num3 >= 0 && num3 < item3.Pieces.Count) { Dump($"{item3.Tier} slab {num3}", item3.Pieces[num3]); } } } capped[(item3.Tier, vanilla)] = item3.Capped; Note(string.Format("overhang {0} {1} ({2}): {3} pieces = {4}{5} m out from the rock (formula {6})", text, item3.Tier, item3.Prefab, item3.Standing, item3.Capped ? "≥ " : "", item3.Standing * 2, item3.Expected)); bool flag = item3.Tier == Tier.Stone || item3.Tier == Tier.BlackMarble || item3.Tier == Tier.Grausten; Check(item3.Standing >= item3.Expected - 1 && (flag || item3.Capped || item3.Standing <= item3.Expected + 1), string.Format("overhang {0} {1}: {2} pieces, the formula says {3} ({4})", text, item3.Tier, item3.Standing, item3.Expected, flag ? "at least" : "±1")); } if (!vanilla) { yield return ((MonoBehaviour)this).StartCoroutine(MainShot("02_overhang", Above(Ground(origin + new Vector3(14f, 0f, 21f)) + Vector3.up * 20f, 205f, 50f, -12f), 60f)); } Clear(rows.SelectMany((Row r) => r.Pieces)); foreach (GameObject item4 in plinths) { Object.Destroy((Object)(object)item4); } yield return (object)new WaitForSecondsRealtime(2f); } (Tier, string)[] beams = Beams; for (int num2 = 0; num2 < beams.Length; num2++) { Tier item2 = beams[num2].Item1; Check(reach[(item2, false)] >= reach[(item2, true)], $"overhang {item2}: mod {reach[(item2, false)]} ≥ vanilla {reach[(item2, true)]}"); } Check(reach[(Tier.Flametal, false)] > reach[(Tier.Iron, false)] || (capped[(Tier.Flametal, false)] && reach[(Tier.Flametal, false)] >= reach[(Tier.Iron, false)]), "flametal reaches farther than iron"); Check(reach[(Tier.Stone, false)] <= 2, "stone still does not reach out (masonry does not bend)"); UseVanilla(vanilla: false); } private IEnumerator Anchoring() { Player localPlayer = Player.m_localPlayer; Vector3 a0 = FlatSpot(((Component)localPlayer).transform.position); ClearArea(a0, 45f); Stand(localPlayer, a0 + new Vector3(-3f, 0f, 7f)); Dictionary<string, float> results = new Dictionary<string, float>(); bool[] array = new bool[2] { true, false }; foreach (bool vanilla in array) { UseVanilla(vanilla); string mode = (vanilla ? "vanilla" : "mod"); Vector3 g = Ground(a0); List<WearNTear> all = new List<WearNTear>(); WearNTear wall = Place("stone_wall_4x2", g + Vector3.up * 0.8f, Quaternion.identity); WearNTear upper = Place("stone_wall_4x2", g + Vector3.up * 2.8f, Quaternion.identity); float num = g.z + 0.5f; Bounds world; WearNTear deep = PlaceAligned("woodiron_beam", new Vector3(g.x - 1.2f, g.y + 1.3f, num - 0.7f + 1f), Vector3.forward, out world); WearNTear shallow = PlaceAligned("woodiron_beam", new Vector3(g.x + 1.2f, g.y + 1.3f, num - 0.3f + 1f), Vector3.forward, out world); WearNTear high = PlaceAligned("woodiron_beam", new Vector3(g.x, g.y + 3.3f, num - 0.7f + 1f), Vector3.forward, out world); WearNTear corbel = PlaceAligned("stone_floor_2x2", new Vector3(g.x, g.y + 1.2f, g.z - 0.5f + 0.7f - 1f), Vector3.forward, out world); all.AddRange((IEnumerable<WearNTear>)(object)new WearNTear[6] { wall, upper, deep, shallow, high, corbel }); yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 6f)); (string, WearNTear)[] array2 = new(string, WearNTear)[6] { ("wall", wall), ("upper", upper), ("deep", deep), ("shallow", shallow), ("high", high), ("corbel", corbel) }; for (int j = 0; j < array2.Length; j++) { var (text, w) = array2[j]; Dump(mode + " " + text, w); } results[mode + " deep"] = Percent(deep); results[mode + " shallow"] = Percent(shallow); results[mode + " high"] = SupportOf(high); results[mode + " upper"] = SupportOf(upper); results[mode + " corbel"] = (Alive(corbel) ? Percent(corbel) : (-1f)); Note($"anchor {mode}: wall {Percent(wall):0}% upper {Percent(upper):0}% ({SupportOf(upper):0}) | iron deep {Percent(deep):0}% shallow {Percent(shallow):0}% " + string.Format("in upper course {0:0}% ({1:0}) | stone corbel {2}", Percent(high), SupportOf(high), Alive(corbel) ? (Percent(corbel).ToString("0") + "%") : "fell")); if (!vanilla) { Note("hover deep: " + Plain(SupportHover.Describe(deep))); Note("hover shallow: " + Plain(SupportHover.Describe(shallow))); Note("hover high: " + Plain(SupportHover.Describe(high))); Note("hover corbel: " + Plain(SupportHover.Describe(corbel))); Note("hover wall: " + Plain(SupportHover.Describe(wall))); Check(SupportHover.Describe(deep).Contains(Lang.T("bs_host_foundation")), "deep beam: hover names the foundation wall"); Check(SupportHover.Describe(shallow).Contains(Lang.T("bs_hover_partial").Substring(0, 5)), "shallow beam: hover tells how much it holds"); } if (!vanilla) { Note($"anchor cost so far: {Anchors.MeasureCount} measurings, {Anchors.MeasureSeconds * 1000.0:0.0} ms in all"); yield return ((MonoBehaviour)this).StartCoroutine(MainShot("03_anchors", Above(g + new Vector3(0f, 1.6f, 0.6f), 35f, 8.5f, 1.2f), 50f)); yield return ((MonoBehaviour)this).StartCoroutine(HammerShot("04_hover_deep", deep)); yield return ((MonoBehaviour)this).StartCoroutine(HammerShot("05_hover_shallow", shallow)); } if (!vanilla) { wall.m_nview.Destroy(); yield return ((MonoBehaviour)this).StartCoroutine(Settle(all.Where(Alive).ToList(), 6f)); array2 = new(string, WearNTear)[5] { ("upper", upper), ("deep", deep), ("shallow", shallow), ("high", high), ("corbel", corbel) }; for (int j = 0; j < array2.Length; j++) { var (text2, w2) = array2[j]; if (Alive(w2)) { Dump("after wall " + text2, w2); } } Check(!Alive(deep) && !Alive(shallow) && !Alive(corbel), $"wall removed: the beams and the corbel set into it fall (deep {Alive(deep)}, shallow {Alive(shallow)}, corbel {Alive(corbel)}; must be False)"); Check(!Alive(upper) && !Alive(high), $"wall removed: the course above and its beam fall (upper {Alive(upper)}, beam {Alive(high)}; must be False)"); } Clear(all); yield return (object)new WaitForSecondsRealtime(2f); } Check(results["mod deep"] >= 99.9f, string.Format("iron beam a third deep in a foundation wall: full support ({0:0} %; vanilla {1:0} %)", results["mod deep"], results["vanilla deep"])); Check(results["vanilla deep"] < 99.9f, string.Format("vanilla gives it less ({0:0} %)", results["vanilla deep"])); Check(results["mod shallow"] < 99.9f && results["mod shallow"] >= results["vanilla shallow"] - 0.5f, string.Format("shallow beam: not full, but never worse than vanilla ({0:0} % vs {1:0} %)", results["mod shallow"], results["vanilla shallow"])); float num2 = results["mod upper"] * 0.97f; Check(Mathf.Abs(results["mod high"] - Mathf.Min(num2, Cfg.Values(Tier.Iron).Max)) <= 0.02f * num2 || results["mod high"] >= num2, string.Format("beam in the upper course takes that wall's support ({0:0} vs wall {1:0} × 0.97)", results["mod high"], results["mod upper"])); Check(results["mod corbel"] >= 99.9f, string.Format("stone corbel set into the wall: full support ({0:0} %; vanilla {1})", results["mod corbel"], (results["vanilla corbel"] < 0f) ? "fell" : (results["vanilla corbel"].ToString("0") + " %"))); UseVanilla(vanilla: false); } private IEnumerator NoFloating() { Player localPlayer = Player.m_localPlayer; Vector3 val = Ground(FlatSpot(((Component)localPlayer).transform.position)); ClearArea(val, 25f); Stand(localPlayer, val + new Vector3(-6f, 0f, 6f)); UseVanilla(vanilla: false); GameObject rock = Plinth(val + Vector3.up * 1.5f, new Vector3(2f, 5f, 2f)); float num = val.y + 4f; WearNTear a = Place("stone_wall_2x1", new Vector3(val.x, num + 0.5f, val.z), Quaternion.identity); Bounds world; WearNTear beam = PlaceAligned("woodiron_beam", new Vector3(val.x + 0.3f, num + 0.5f, val.z + 0.5f - 0.7f + 1f), Vector3.forward, out world); WearNTear b = Place("stone_wall_2x1", new Vector3(val.x + 0.3f, num + 0.65f + 0.5f, val.z + 1.2f), Quaternion.identity); WearNTear s1 = PlaceAligned("stone_floor_2x2", new Vector3(val.x - 0.3f, num + 1.5f, val.z - 1.2f), Vector3.right, out world); WearNTear s2 = PlaceAligned("stone_floor_2x2", new Vector3(val.x - 0.3f + 1.4f, num + 1.6f, val.z - 1.2f), Vector3.right, out world); List<WearNTear> all = new List<WearNTear> { a, beam, b, s1, s2 }; yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 5f, 90f)); Note($"before: wall {Percent(a):0}% beam {Percent(beam):0}% wall on beam {Alive(b)} slabs {Alive(s1)}/{Alive(s2)}"); Check(Alive(a) && Alive(beam) && Percent(beam) >= 99.9f, "on the rock: the wall is a foundation, its beam has full support"); Object.Destroy((Object)(object)rock); float t0 = Time.realtimeSinceStartup; yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 6f, 180f)); Note(string.Format("after the rock went ({0:0} s): alive {1}", Time.realtimeSinceStartup - t0, string.Join(" ", all.Select((WearNTear w) => (!Alive(w)) ? "no" : "yes")))); foreach (WearNTear item in all.Where(Alive)) { Dump("floating", item); } Check(all.All((WearNTear w) => !Alive(w)), "rock gone: nothing holds itself up in the air (all must have fallen)"); Clear(all); } private IEnumerator Cliff() { Player p = Player.m_localPlayer; Vector3 facePoint = Vector3.zero; Vector3 uphill = Vector3.zero; string kind = ""; foreach (var (at, dir) in CliffCandidates(((Component)p).transform.position)) { Stand(p, at - dir * 8f); float until = Time.realtimeSinceStartup + 60f; while (!GroundLoaded(at) && Time.realtimeSinceStartup < until) { yield return null; } yield return (object)new WaitForSecondsRealtime(4f); if (FindRockFace(at, out var hit, out var into, out var name)) { facePoint = hit; uphill = into; kind = "rock " + name; break; } Vector3 val = Ground(at - dir * 2f); float num = Ground(at + dir * 2f).y - val.y; Note($"cliff candidate {at:F0}: no rock face; ground rises {num:0.0} m over 4 m"); if (!(num >= 2.5f) || !(kind == "")) { continue; } for (float num2 = 0f; num2 < 6f; num2 += 0.05f) { Vector3 val2 = Ground(val + dir * num2); if (val2.y >= val.y + 1.8f) { facePoint = val2; break; } } uphill = dir; kind = $"ground slope ({Mathf.Atan2(num, 4f) * 57.29578f:0}°)"; } Note("cliff: building against " + kind); if (uphill == Vector3.zero) { Check(ok: false, "cliff: no steep rock face found near the start"); yield break; } Vector3 val3 = Vector3.Cross(Vector3.up, uphill); Stand(p, facePoint - uphill * 9f + val3 * 4f); float loaded = Time.realtimeSinceStartup + 60f; while (!GroundLoaded(facePoint) && Time.realtimeSinceStartup < loaded) { yield return null; } yield return (object)new WaitForSecondsRealtime(3f); ClearArea(facePoint, 40f); float y = facePoint.y; Note($"cliff: face at {facePoint}, ground 1 m in front {Ground(facePoint - uphill).y - y:+0.0;-0.0} m, 1 m behind {Ground(facePoint + uphill).y - y:+0.0;-0.0} m"); yield return ((MonoBehaviour)this).StartCoroutine(BuildAgainst(facePoint, uphill, kind, "06_cliff", "07_hover_rock", slopeWall: true)); yield return ((MonoBehaviour)this).StartCoroutine(RockFace()); UseVanilla(vanilla: false); } private IEnumerator RockFace() { Player localPlayer = Player.m_localPlayer; GameObject pf = (from g in ZNetScene.instance.m_prefabs where (Object)(object)g != (Object)null && ((Object)g).name.IndexOf("rock", StringComparison.OrdinalIgnoreCase) >= 0 && (Object)(object)g.GetComponentInChildren<MineRock>() == (Object)null && (Object)(object)g.GetComponentInChildren<MineRock5>() == (Object)null && (Object)(object)g.GetComponentInChildren<Destructible>() == (Object)null && (Object)(object)g.GetComponentInChildren<WearNTear>() == (Object)null && (Object)(object)g.GetComponentInChildren<Pickable>() == (Object)null select (g: g, size: RockSize(g)) into x where x.size.y >= 5f && Mathf.Min(x.size.x, x.size.z) >= 5f orderby ((Vector3)(ref x.size)).magnitude select x.g).FirstOrDefault(); if ((Object)(object)pf == (Object)null) { Check(ok: false, "rock face: no fixed rock prefab with a concave collider found"); yield break; } Vector3 site = FlatSpot(((Component)localPlayer).transform.position); Stand(localPlayer, site + new Vector3(-14f, 0f, 0f)); float loaded = Time.realtimeSinceStartup + 60f; while (!GroundLoaded(site) && Time.realtimeSinceStartup < loaded) { yield return null; } yield return (object)new WaitForSecondsRealtime(3f); ClearArea(site, 35f); GameObject rock = Object.Instantiate<GameObject>(pf, Ground(site) + Vector3.down * 1f, Quaternion.identity); yield return (object)new WaitForSecondsRealtime(1f); Note($"rock face: spawned {((Object)pf).name}, size {RockSize(pf):F1}"); if (!FindRockFace(site, out var hit, out var into, out var name, 4.5f)) { Check(ok: false, "rock face: no steep side on " + ((Object)pf).name); yield break; } yield return ((MonoBehaviour)this).StartCoroutine(BuildAgainst(hit, into, "rock " + name, "08_rock", "09_hover_rock", slopeWall: false)); ZNetView component = rock.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.Destroy(); } else { Object.Destroy((Object)(object)rock); } } private static Vector3 RockSize(GameObject g) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_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_0058: 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_006c: Unknown result type (might be due to invalid IL or missing references) Vector3 result = Vector3.zero; MeshCollider[] componentsInChildren = g.GetComponentsInChildren<MeshCollider>(true); foreach (MeshCollider val in componentsInChildren) { if (!val.convex && !((Collider)val).isTrigger && !((Object)(object)val.sharedMesh == (Object)null)) { Bounds bounds = val.sharedMesh.bounds; Vector3 val2 = Vector3.Scale(((Bounds)(ref bounds)).size, ((Component)val).transform.lossyScale); if (((Vector3)(ref val2)).magnitude > ((Vector3)(ref result)).magnitude) { result = val2; } } } return result; } private IEnumerator BuildAgainst(Vector3 facePoint, Vector3 uphill, string kind, string shot, string hoverShot, bool slopeWall) { //IL_000e: 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_0015: 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) Vector3 side = Vector3.Cross(Vector3.up, uphill); bool[] array = new bool[2] { true, false }; foreach (bool vanilla in array) { UseVanilla(vanilla); string mode = (vanilla ? "vanilla" : "mod"); List<WearNTear> all = new List<WearNTear>(); List<WearNTear> inner = new List<WearNTear>(); float[] array2 = new float[2] { -1f, 1f }; Bounds world; foreach (float num in array2) { Vector3 val = facePoint + side * num + Vector3.up * 0.05f; WearNTear item = PlaceAligned("woodiron_beam", val - uphill * 0.19999999f, -uphill, out world); WearNTear item2 = PlaceAligned("woodiron_beam", val - uphill * 2.2f, -uphill, out world); inner.Add(item); all.Add(item); all.Add(item2); } Quaternion rot = Quaternion.LookRotation(-uphill); for (int k = 0; k < 2; k++) { Vector3 pos = facePoint - uphill * (1.6f + (float)k * 2f) + Vector3.up * 0.25f; all.Add(Place("wood_floor", pos, rot)); } Vector3 pos2 = facePoint - uphill * 4.6f + Vector3.up * 1.3f; all.Add(Place("woodwall", pos2, rot)); all.Add(Place("woodwall", facePoint - uphill * 3.6f + side * 1f + Vector3.up * 1.3f, Quaternion.LookRotation(side))); all.Add(Place("woodwall", facePoint - uphill * 3.6f - side * 1f + Vector3.up * 1.3f, Quaternion.LookRotation(side))); all.RemoveAll((WearNTear w) => (Object)(object)w == (Object)null); Vector3 at = facePoint + side * 5f; WearNTear val2 = ((!slopeWall) ? null : Place("stone_wall_2x1", Ground(at) + Vector3.up * 0.3f - uphill * 0.2f, Quaternion.LookRotation(side))); WearNTear swBeam = (((Object)(object)val2 == (Object)null) ? null : PlaceAligned("woodiron_beam", ((Component)val2).transform.position - uphill * 0.8f + Vector3.up * 0.2f, -uphill, out world)); all.Add(val2); all.Add(swBeam); all.RemoveAll((WearNTear w) => (Object)(object)w == (Object)null); yield return ((MonoBehaviour)this).StartCoroutine(Settle(all, 6f)); int num2 = all.Count(Alive); Note(string.Format("{0} {1}: {2} of {3} pieces stand; rock beams {4}, ", kind, mode, num2, all.Count, string.Join(" ", inner.Select((WearNTear w) => Percent(w).ToString("0") + "%"))) + (slopeWall ? $"beam in the stone wall {Percent(swBeam):0}%" : "")); if (!vanilla) { Check(num2 == all.Count, $"{kind}: house on beams set into it, everything stands ({num2} of {all.Count})"); Check(inner.All((WearNTear w) => Percent(w) >= 99.9f), kind + ": beams set into it have full support"); if (slopeWall) { Check(Percent(swBeam) >= 99.9f, $"beam in a stone wall set into the slope: full support ({Percent(swBeam):0} %)"); } Note("hover rock beam: " + Plain(SupportHover.Describe(inner[0]))); string text = Lang.T(kind.StartsWith("rock") ? "bs_host_rock" : "bs_host_ground"); Check(SupportHover.Describe(inner[0]).Contains(text), "rock beam: hover says it is set into " + text); EnvMan.instance.m_debugTimeOfDay = true; EnvMan.instance.m_debugTime = 0.62f; yield return (object)new WaitForSecondsRealtime(1f); Vector3 target = facePoint - uphill * 2.5f + Vector3.up * 1.2f; Quaternion val3 = Quaternion.LookRotation(-uphill); float bearing = ((Quaternion)(ref val3)).eulerAngles.y + 35f; yield return ((MonoBehaviour)this).StartCoroutine(MainShot(shot, Above(target, bearing, 10f, 1.5f), 58f)); yield return ((MonoBehaviour)this).StartCoroutine(HammerShot(hoverShot, inner[0])); } Clear(all); yield return (object)new WaitForSecondsRealtime(2f); } UseVanilla(vanilla: false); } private bool FindRockFace(Vector3 at, out Vector3 hit, out Vector3 into, out string name, float height = 3f) { //IL_0007: 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_0015: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: 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_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019f: 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_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: 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_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: 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_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references)