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 Treadwell v0.1.2
plugins/Treadwell/Treadwell.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Treadwell.Core; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("JStack424")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Treadwell Valheim plugin.")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2+8583a261d57d056dff56c24aee6a3d8fad92ebfd")] [assembly: AssemblyProduct("Treadwell")] [assembly: AssemblyTitle("Treadwell")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/JStack424/Treadwell")] [assembly: AssemblyVersion("0.1.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Treadwell { internal sealed class CompatibilityResult { internal bool IsCompatible { get; } internal string Reason { get; } internal CompatibilityResult(bool isCompatible, string reason) { IsCompatible = isCompatible; Reason = reason; } } internal static class CompatibilityGate { internal static CompatibilityResult Evaluate(FeatureHost features) { List<string> list = new List<string>(); try { features.ValidateCompatibility(list); } catch (Exception ex) { list.Add("feature compatibility validation threw: " + ex.GetType().Name); } if (list.Count != 0) { return new CompatibilityResult(isCompatible: false, string.Join("; ", list) + " (" + RuntimeDiagnostics() + ")"); } return new CompatibilityResult(isCompatible: true, "runtime contract verified (" + RuntimeDiagnostics() + ")"); } internal static void RequireMethod(ICollection<string> failures, Type declaringType, string name, Type returnType, BindingFlags flags, Type[] parameterTypes, Func<MethodInfo, bool> additionalCheck = null) { MethodInfo[] array; try { array = ExactRuntimeContract.FindMethods(declaringType, name, flags, returnType, parameterTypes, additionalCheck); } catch (Exception ex) { failures.Add(declaringType.Name + "." + name + " contract inspection threw: " + ex.GetType().Name); return; } if (array.Length != 1) { failures.Add(declaringType.Name + "." + name + " requires one exact runtime signature; found " + array.Length); } } internal static void RequireConstructor(ICollection<string> failures, Type declaringType, BindingFlags flags, Type[] parameterTypes) { ConstructorInfo[] array; try { array = ExactRuntimeContract.FindConstructors(declaringType, flags, parameterTypes); } catch (Exception ex) { failures.Add(declaringType.Name + " constructor inspection threw: " + ex.GetType().Name); return; } if (array.Length != 1) { failures.Add(declaringType.Name + " requires one exact runtime constructor; found " + array.Length); } } internal static void RequireMethodNamedReturn(ICollection<string> failures, Type declaringType, string name, string returnTypeFullName, BindingFlags flags, Type[] parameterTypes) { MethodInfo[] array; try { array = (from method in declaringType.GetMethods(flags) where string.Equals(method.Name, name, StringComparison.Ordinal) where string.Equals(method.ReturnType.FullName, returnTypeFullName, StringComparison.Ordinal) where ParametersMatch(method.GetParameters(), parameterTypes) select method).ToArray(); } catch (Exception ex) { failures.Add(declaringType.Name + "." + name + " contract inspection threw: " + ex.GetType().Name); return; } if (array.Length != 1) { failures.Add(declaringType.Name + "." + name + " requires one exact runtime signature; found " + array.Length); } } internal static void RequirePatchMethod(ICollection<string> failures, Type declaringType, string name, Type[] parameterTypes) { RequireMethod(failures, declaringType, name, typeof(void), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic, parameterTypes, (MethodInfo method) => method.IsStatic); } internal static void RequireField(ICollection<string> failures, Type declaringType, string name, Type fieldType, BindingFlags flags) { FieldInfo[] array; try { array = ExactRuntimeContract.FindFields(declaringType, name, flags | BindingFlags.DeclaredOnly, fieldType); } catch (Exception ex) { failures.Add(declaringType.Name + "." + name + " field inspection threw: " + ex.GetType().Name); return; } if (array.Length != 1) { failures.Add(declaringType.Name + "." + name + " requires one exact runtime field; found " + array.Length); } } internal static void RequireProperty(ICollection<string> failures, Type declaringType, string name, Type propertyType, BindingFlags flags, bool requireGetter, bool requireSetter) { PropertyInfo[] array; try { array = (from property in ExactRuntimeContract.FindProperties(declaringType, name, flags, propertyType) where property.GetIndexParameters().Length == 0 where !requireGetter || property.GetGetMethod(nonPublic: true) != null where !requireSetter || property.GetSetMethod(nonPublic: true) != null select property).ToArray(); } catch (Exception ex) { failures.Add(declaringType.Name + "." + name + " property inspection threw: " + ex.GetType().Name); return; } if (array.Length != 1) { failures.Add(declaringType.Name + "." + name + " requires one exact runtime property; found " + array.Length); } } internal static void RequireGenericMethod(ICollection<string> failures, Type declaringType, string name, BindingFlags flags, Type[] parameterTypes, bool returnsArray) { MethodInfo[] array; try { array = (from method in declaringType.GetMethods(flags) where string.Equals(method.Name, name, StringComparison.Ordinal) where method.IsGenericMethodDefinition && method.GetGenericArguments().Length == 1 where ParametersMatch(method.GetParameters(), parameterTypes) where GenericReturnMatches(method.ReturnType, returnsArray) select method).ToArray(); } catch (Exception ex) { failures.Add(declaringType.Name + "." + name + " generic contract inspection threw: " + ex.GetType().Name); return; } if (array.Length != 1) { failures.Add(declaringType.Name + "." + name + " requires one exact generic runtime signature; found " + array.Length); } } internal static void RequireEnumValue(ICollection<string> failures, Type enumType, string name, int expectedValue) { try { if (!enumType.IsEnum || !Enum.IsDefined(enumType, name) || Convert.ToInt32(Enum.Parse(enumType, name)) != expectedValue) { failures.Add(enumType.Name + "." + name + " enum value is incompatible"); } } catch (Exception ex) { failures.Add(enumType.Name + "." + name + " enum inspection threw: " + ex.GetType().Name); } } internal static void RequireColor(ICollection<string> failures, string label, Color observed, float red, float green, float blue, float alpha) { //IL_0000: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (Math.Abs(observed.r - red) > 0.0001f || Math.Abs(observed.g - green) > 0.0001f || Math.Abs(observed.b - blue) > 0.0001f || Math.Abs(observed.a - alpha) > 0.0001f) { failures.Add(label + " encoding is incompatible"); } } private static bool ParametersMatch(ParameterInfo[] observed, Type[] expected) { if (observed.Length != expected.Length) { return false; } for (int i = 0; i < observed.Length; i++) { if (observed[i].ParameterType != expected[i]) { return false; } } return true; } private static bool GenericReturnMatches(Type returnType, bool returnsArray) { if (!returnsArray) { if (returnType.IsGenericParameter) { return returnType.GenericParameterPosition == 0; } return false; } Type elementType = returnType.GetElementType(); if (returnType.IsArray && elementType != null && elementType.IsGenericParameter) { return elementType.GenericParameterPosition == 0; } return false; } private static string RuntimeDiagnostics() { string text = ReadStaticDiagnosticProperty(typeof(Player).Assembly.GetType("Version", throwOnError: false), "CurrentVersion"); string text2 = ReadStaticDiagnosticProperty(typeof(Application), "unityVersion"); string text3 = SafeDiagnostic(() => typeof(BaseUnityPlugin).Assembly.GetName().Version?.ToString()); string text4 = SafeDiagnostic(() => typeof(Harmony).Assembly.GetName().Version?.ToString()); string text5 = SafeDiagnostic(() => typeof(Player).Assembly.ManifestModule.ModuleVersionId.ToString()); return "game " + text + ", Unity " + text2 + ", BepInEx " + text3 + ", Harmony " + text4 + ", assembly MVID " + text5; } private static string ReadStaticDiagnosticProperty(Type declaringType, string name) { if (declaringType == null) { return "unknown"; } return SafeDiagnostic(delegate { PropertyInfo[] array = (from property in declaringType.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(property.Name, name, StringComparison.Ordinal) where property.GetIndexParameters().Length == 0 && property.GetGetMethod(nonPublic: true) != null select property).ToArray(); return (array.Length != 1) ? "unknown" : array[0].GetValue(null, null)?.ToString(); }); } private static string SafeDiagnostic(Func<string> read) { try { return read() ?? "unknown"; } catch { return "unknown"; } } } internal sealed class FeatureHost : IDisposable { private readonly IReadOnlyList<IFeatureModule> _modules; private readonly ManualLogSource _log; private bool _started; internal FeatureHost(IEnumerable<IFeatureModule> modules, ManualLogSource log) { _modules = (modules ?? throw new ArgumentNullException("modules")).ToArray(); _log = log ?? throw new ArgumentNullException("log"); if (_modules.Select((IFeatureModule module) => module.Id).Distinct<string>(StringComparer.Ordinal).Count() != _modules.Count) { throw new InvalidOperationException("Feature module ids must be unique."); } } internal void ValidateCompatibility(ICollection<string> failures) { foreach (IFeatureModule module in _modules) { module.ValidateCompatibility(failures); } } internal void Start() { if (_started) { return; } try { foreach (IFeatureModule module in _modules) { module.Enabled.SettingChanged += OnSettingChanged; if (module.Enabled.Value) { module.Enable(); } } _started = true; } catch { Stop(); throw; } } private void OnSettingChanged(object sender, EventArgs eventArgs) { try { foreach (IFeatureModule module in _modules) { if (module.Enabled.Value) { module.Enable(); } else { module.Disable(); } } } catch (Exception ex) { _log.LogError((object)("A feature toggle failed; all feature modules were disabled: " + ex)); Stop(); } } internal void Stop() { foreach (IFeatureModule item in _modules.Reverse()) { item.Enabled.SettingChanged -= OnSettingChanged; try { item.Disable(); } catch (Exception ex) { _log.LogError((object)("Feature cleanup failed for " + item.Id + ": " + ex)); } } _started = false; } public void Dispose() { Stop(); } } internal interface IFeatureModule { string Id { get; } ConfigEntry<bool> Enabled { get; } void ValidateCompatibility(ICollection<string> failures); void Enable(); void Disable(); } internal abstract class FeatureModuleBase : IFeatureModule { private readonly string _harmonyId; private Harmony _harmony; private bool _active; private bool _cleanupPending; public string Id { get; } public ConfigEntry<bool> Enabled { get; } protected ManualLogSource Log { get; } protected Harmony Harmony => _harmony ?? throw new InvalidOperationException("Harmony was not initialized after compatibility validation."); protected FeatureModuleBase(string id, ConfigEntry<bool> enabled, ManualLogSource log) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentException("Feature id is required.", "id"); } Id = id; Enabled = enabled ?? throw new ArgumentNullException("enabled"); Log = log ?? throw new ArgumentNullException("log"); _harmonyId = "com.jstack424.treadwell.feature." + id; } public abstract void ValidateCompatibility(ICollection<string> failures); public void Enable() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown if (_active) { return; } if (_cleanupPending) { Disable(); } _cleanupPending = true; try { _harmony = new Harmony(_harmonyId); TransactionalInstall.Run(InstallPatches, delegate { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } }, OnDisabled); _active = true; _cleanupPending = false; Log.LogInfo((object)("Enabled feature module: " + Id)); } catch { _active = false; throw; } } public void Disable() { if (!_active && !_cleanupPending) { return; } List<Exception> list = new List<Exception>(); try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception item) { list.Add(item); } try { OnDisabled(); } catch (Exception item2) { list.Add(item2); } _active = false; if (list.Count > 0) { _cleanupPending = true; throw new AggregateException("Feature cleanup failed for " + Id + ".", list); } _harmony = null; _cleanupPending = false; Log.LogInfo((object)("Disabled feature module: " + Id)); } protected abstract void InstallPatches(); protected virtual void OnDisabled() { } } internal sealed class RoadFeatureModule : FeatureModuleBase { private sealed class PieceTableEntryInspection { internal GameObject Root { get; } internal Piece[] Pieces { get; } internal TerrainModifier[] Modifiers { get; } internal PavedRoadCandidateShape Shape { get; } internal Piece Piece { get { if (Pieces.Length != 1) { return null; } return Pieces[0]; } } internal PieceTableEntryInspection(GameObject root, Piece[] pieces, TerrainModifier[] modifiers, PavedRoadCandidateShape shape) { Root = root; Pieces = pieces; Modifiers = modifiers; Shape = shape; } internal string Describe() { Piece piece = Piece; StringBuilder stringBuilder = new StringBuilder("[root='"); stringBuilder.Append(SanitizeDiagnostic(((Object)(object)Root != (Object)null) ? ((Object)Root).name : null)); stringBuilder.Append("', pieces=").Append(Pieces.Length); stringBuilder.Append(", piece='").Append(SanitizeDiagnostic(((Object)(object)piece != (Object)null) ? piece.m_name : null)).Append("'"); stringBuilder.Append(", station='"); stringBuilder.Append(SanitizeDiagnostic(((Object)(object)piece != (Object)null && (Object)(object)piece.m_craftingStation != (Object)null && (Object)(object)((Component)piece.m_craftingStation).gameObject != (Object)null) ? ((Object)((Component)piece.m_craftingStation).gameObject).name : null)); stringBuilder.Append("', terrain="); AppendTerrainSummary(stringBuilder, Modifiers); stringBuilder.Append(", resources="); AppendResourceSummary(stringBuilder, ((Object)(object)piece != (Object)null) ? piece.m_resources : null); stringBuilder.Append(']'); return stringBuilder.ToString(); } private static void AppendTerrainSummary(StringBuilder text, TerrainModifier[] modifiers) { text.Append('['); int num = Math.Min(modifiers.Length, 4); for (int i = 0; i < num; i++) { if (i > 0) { text.Append(','); } text.Append(((Object)(object)modifiers[i] != (Object)null) ? ((object)Unsafe.As<PaintType, PaintType>(ref modifiers[i].m_paintType)/*cast due to .constrained prefix*/).ToString() : "<null>"); } if (modifiers.Length > num) { text.Append(",+").Append(modifiers.Length - num); } text.Append(']'); } private static void AppendResourceSummary(StringBuilder text, Requirement[] requirements) { text.Append('['); if (requirements != null) { int num = Math.Min(requirements.Length, 4); for (int i = 0; i < num; i++) { if (i > 0) { text.Append(','); } Requirement val = requirements[i]; string value = ((val != null && (Object)(object)val.m_resItem != (Object)null && (Object)(object)((Component)val.m_resItem).gameObject != (Object)null) ? ((Object)((Component)val.m_resItem).gameObject).name : null); text.Append(SanitizeDiagnostic(value)).Append(':').Append(val?.m_amount ?? 0); } if (requirements.Length > num) { text.Append(",+").Append(requirements.Length - num); } } text.Append(']'); } } internal const double NaturalGapHoldSeconds = 0.18; private static RoadFeatureModule _activeModule; private static MethodInfo GetLastGroundColliderMethod; private static MethodInfo UpdateAvailablePiecesListMethod; private readonly ConfigEntry<bool> _pavedRoadWithoutStonecutter; private readonly ConfigEntry<float> _dirtSpeed; private readonly ConfigEntry<float> _dirtStamina; private readonly ConfigEntry<float> _pavedSpeed; private readonly ConfigEntry<float> _pavedStamina; private readonly RoadSurfaceTracker _surfaceTracker = new RoadSurfaceTracker(0.18); private readonly PavedRoadStationOverride<Piece, CraftingStation> _stationOverride; private PieceTable _lastPieceTable; private bool _pavedSettingSubscribed; private bool _loggedStationRemoved; private bool _loggedStationAlreadyAbsent; private bool _loggedDiscoveryFailure; private bool _loggedStationConflict; internal RoadFeatureModule(ConfigEntry<bool> enabled, ConfigEntry<bool> pavedRoadWithoutStonecutter, ConfigEntry<float> dirtSpeed, ConfigEntry<float> dirtStamina, ConfigEntry<float> pavedSpeed, ConfigEntry<float> pavedStamina, ManualLogSource log) : base("roads", enabled, log) { _pavedRoadWithoutStonecutter = pavedRoadWithoutStonecutter ?? throw new ArgumentNullException("pavedRoadWithoutStonecutter"); _dirtSpeed = dirtSpeed ?? throw new ArgumentNullException("dirtSpeed"); _dirtStamina = dirtStamina ?? throw new ArgumentNullException("dirtStamina"); _pavedSpeed = pavedSpeed ?? throw new ArgumentNullException("pavedSpeed"); _pavedStamina = pavedStamina ?? throw new ArgumentNullException("pavedStamina"); _stationOverride = new PavedRoadStationOverride<Piece, CraftingStation>((Piece piece) => piece.m_craftingStation, delegate(Piece piece, CraftingStation? station) { piece.m_craftingStation = station; }); } public override void ValidateCompatibility(ICollection<string> failures) { //IL_0a6a: Unknown result type (might be due to invalid IL or missing references) //IL_0a8e: Unknown result type (might be due to invalid IL or missing references) //IL_0ab2: Unknown result type (might be due to invalid IL or missing references) CompatibilityGate.RequireMethod(failures, typeof(PieceTable), "UpdateAvailable", typeof(void), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, new Type[4] { typeof(HashSet<string>), typeof(Player), typeof(bool), typeof(bool) }, (MethodInfo method) => !method.IsStatic); CompatibilityGate.RequireField(failures, typeof(PieceTable), "m_pieces", typeof(List<GameObject>), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireMethod(failures, typeof(ZNetScene), "OnDestroy", typeof(void), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, Type.EmptyTypes, (MethodInfo method) => !method.IsStatic); CompatibilityGate.RequireMethod(failures, typeof(Player), "SetPlaceMode", typeof(void), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, new Type[1] { typeof(PieceTable) }, (MethodInfo method) => !method.IsStatic && method.IsFamily && method.IsVirtual); CompatibilityGate.RequireMethod(failures, typeof(Player), "GetBuildTool", typeof(PieceTable), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes, (MethodInfo method) => !method.IsStatic); CompatibilityGate.RequireMethod(failures, typeof(Player), "UpdateAvailablePiecesList", typeof(void), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, Type.EmptyTypes, (MethodInfo method) => !method.IsStatic); CompatibilityGate.RequireMethod(failures, typeof(Player), "HaveRequirements", typeof(bool), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, new Type[2] { typeof(Piece), typeof(RequirementMode) }, (MethodInfo method) => !method.IsStatic); CompatibilityGate.RequireField(failures, typeof(Piece), "m_name", typeof(string), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Piece), "m_craftingStation", typeof(CraftingStation), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Piece), "m_resources", typeof(Requirement[]), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Requirement), "m_resItem", typeof(ItemDrop), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Requirement), "m_amount", typeof(int), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(TerrainModifier), "m_paintType", typeof(PaintType), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireMethod(failures, typeof(Player), "GetRunSpeedFactor", typeof(float), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, Type.EmptyTypes, (MethodInfo method) => method.IsFamily && method.IsVirtual); CompatibilityGate.RequireMethod(failures, typeof(SEMan), "ModifyRunStaminaDrain", typeof(void), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, new Type[4] { typeof(float), typeof(float).MakeByRefType(), typeof(Vector3), typeof(bool) }, (MethodInfo method) => !method.IsStatic && string.Equals(method.GetParameters()[1].Name, "drain", StringComparison.Ordinal)); CompatibilityGate.RequireMethod(failures, typeof(Character), "IsOnGround", typeof(bool), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes); CompatibilityGate.RequireMethod(failures, typeof(Character), "IsRunning", typeof(bool), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes); CompatibilityGate.RequireMethodNamedReturn(failures, typeof(Character), "GetLastGroundCollider", "UnityEngine.Collider", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes); CompatibilityGate.RequireMethod(failures, typeof(Heightmap), "GetPaintMask", typeof(Color), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, new Type[1] { typeof(Vector3) }); CompatibilityGate.RequireField(failures, typeof(Player), "m_localPlayer", typeof(Player), BindingFlags.Static | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(SEMan), "m_character", typeof(Character), BindingFlags.Instance | BindingFlags.NonPublic); CompatibilityGate.RequireField(failures, typeof(Heightmap), "m_paintMaskDirt", typeof(Color), BindingFlags.Static | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Heightmap), "m_paintMaskCultivated", typeof(Color), BindingFlags.Static | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Heightmap), "m_paintMaskPaved", typeof(Color), BindingFlags.Static | BindingFlags.Public); CompatibilityGate.RequirePatchMethod(failures, typeof(RoadFeatureModule), "PlayerSetPlaceModePrefix", new Type[1] { typeof(PieceTable) }); CompatibilityGate.RequirePatchMethod(failures, typeof(RoadFeatureModule), "PieceTableUpdateAvailablePrefix", new Type[1] { typeof(PieceTable) }); CompatibilityGate.RequirePatchMethod(failures, typeof(RoadFeatureModule), "PlayerHaveRequirementsPrefix", new Type[1] { typeof(Piece) }); CompatibilityGate.RequirePatchMethod(failures, typeof(RoadFeatureModule), "ZNetSceneOnDestroyPrefix", Type.EmptyTypes); CompatibilityGate.RequirePatchMethod(failures, typeof(RoadFeatureModule), "GetRunSpeedFactorPostfix", new Type[2] { typeof(Player), typeof(float).MakeByRefType() }); CompatibilityGate.RequirePatchMethod(failures, typeof(RoadFeatureModule), "ModifyRunStaminaDrainPostfix", new Type[2] { typeof(Character), typeof(float).MakeByRefType() }); CompatibilityGate.RequireGenericMethod(failures, typeof(GameObject), "GetComponent", BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes, returnsArray: false); CompatibilityGate.RequireGenericMethod(failures, typeof(GameObject), "GetComponentsInChildren", BindingFlags.Instance | BindingFlags.Public, new Type[1] { typeof(bool) }, returnsArray: true); CompatibilityGate.RequireGenericMethod(failures, typeof(Component), "GetComponent", BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes, returnsArray: false); CompatibilityGate.RequireGenericMethod(failures, typeof(Component), "GetComponentInParent", BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes, returnsArray: false); CompatibilityGate.RequireProperty(failures, typeof(Component), "gameObject", typeof(GameObject), BindingFlags.Instance | BindingFlags.Public, requireGetter: true, requireSetter: false); CompatibilityGate.RequireProperty(failures, typeof(Component), "transform", typeof(Transform), BindingFlags.Instance | BindingFlags.Public, requireGetter: true, requireSetter: false); CompatibilityGate.RequireProperty(failures, typeof(Transform), "position", typeof(Vector3), BindingFlags.Instance | BindingFlags.Public, requireGetter: true, requireSetter: false); CompatibilityGate.RequireProperty(failures, typeof(Time), "unscaledTime", typeof(float), BindingFlags.Static | BindingFlags.Public, requireGetter: true, requireSetter: false); CompatibilityGate.RequireProperty(failures, typeof(Object), "name", typeof(string), BindingFlags.Instance | BindingFlags.Public, requireGetter: true, requireSetter: false); CompatibilityGate.RequireField(failures, typeof(Color), "r", typeof(float), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Color), "g", typeof(float), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Color), "b", typeof(float), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireField(failures, typeof(Color), "a", typeof(float), BindingFlags.Instance | BindingFlags.Public); CompatibilityGate.RequireMethod(failures, typeof(Object), "op_Equality", typeof(bool), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, new Type[2] { typeof(Object), typeof(Object) }, (MethodInfo method) => method.IsStatic); CompatibilityGate.RequireMethod(failures, typeof(Object), "op_Inequality", typeof(bool), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, new Type[2] { typeof(Object), typeof(Object) }, (MethodInfo method) => method.IsStatic); CompatibilityGate.RequireConstructor(failures, typeof(Harmony), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, new Type[1] { typeof(string) }); CompatibilityGate.RequireConstructor(failures, typeof(HarmonyMethod), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, new Type[3] { typeof(Type), typeof(string), typeof(Type[]) }); CompatibilityGate.RequireMethod(failures, typeof(Harmony), "Patch", typeof(MethodInfo), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, new Type[6] { typeof(MethodBase), typeof(HarmonyMethod), typeof(HarmonyMethod), typeof(HarmonyMethod), typeof(HarmonyMethod), typeof(HarmonyMethod) }, (MethodInfo method) => !method.IsStatic); CompatibilityGate.RequireMethod(failures, typeof(Harmony), "UnpatchSelf", typeof(void), BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, Type.EmptyTypes, (MethodInfo method) => !method.IsStatic); CompatibilityGate.RequireMethod(failures, typeof(AccessTools), "DeclaredMethod", typeof(MethodInfo), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, new Type[4] { typeof(Type), typeof(string), typeof(Type[]), typeof(Type[]) }, (MethodInfo method) => method.IsStatic); CompatibilityGate.RequireEnumValue(failures, typeof(PaintType), "Dirt", 0); CompatibilityGate.RequireEnumValue(failures, typeof(PaintType), "Cultivate", 1); CompatibilityGate.RequireEnumValue(failures, typeof(PaintType), "Paved", 2); CompatibilityGate.RequireColor(failures, "dirt paint", Heightmap.m_paintMaskDirt, 1f, 0f, 0f, 1f); CompatibilityGate.RequireColor(failures, "cultivated paint", Heightmap.m_paintMaskCultivated, 0f, 1f, 0f, 1f); CompatibilityGate.RequireColor(failures, "paved paint", Heightmap.m_paintMaskPaved, 0f, 0f, 1f, 1f); } protected override void InstallPatches() { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Expected O, but got Unknown //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Expected O, but got Unknown if (_activeModule != null && _activeModule != this) { throw new InvalidOperationException("Another road feature module is already active."); } GetLastGroundColliderMethod = AccessTools.DeclaredMethod(typeof(Character), "GetLastGroundCollider", Type.EmptyTypes, (Type[])null) ?? throw new MissingMethodException(typeof(Character).FullName, "GetLastGroundCollider"); UpdateAvailablePiecesListMethod = AccessTools.DeclaredMethod(typeof(Player), "UpdateAvailablePiecesList", Type.EmptyTypes, (Type[])null) ?? throw new MissingMethodException(typeof(Player).FullName, "UpdateAvailablePiecesList"); _activeModule = this; base.Harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Player), "SetPlaceMode", new Type[1] { typeof(PieceTable) }, (Type[])null), new HarmonyMethod(typeof(RoadFeatureModule), "PlayerSetPlaceModePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); base.Harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(PieceTable), "UpdateAvailable", new Type[4] { typeof(HashSet<string>), typeof(Player), typeof(bool), typeof(bool) }, (Type[])null), new HarmonyMethod(typeof(RoadFeatureModule), "PieceTableUpdateAvailablePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); base.Harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Player), "HaveRequirements", new Type[2] { typeof(Piece), typeof(RequirementMode) }, (Type[])null), new HarmonyMethod(typeof(RoadFeatureModule), "PlayerHaveRequirementsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); base.Harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(ZNetScene), "OnDestroy", Type.EmptyTypes, (Type[])null), new HarmonyMethod(typeof(RoadFeatureModule), "ZNetSceneOnDestroyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); base.Harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Player), "GetRunSpeedFactor", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(RoadFeatureModule), "GetRunSpeedFactorPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); base.Harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(SEMan), "ModifyRunStaminaDrain", new Type[4] { typeof(float), typeof(float).MakeByRefType(), typeof(Vector3), typeof(bool) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(RoadFeatureModule), "ModifyRunStaminaDrainPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _pavedRoadWithoutStonecutter.SettingChanged += OnPavedRoadSettingChanged; _pavedSettingSubscribed = true; RefreshCurrentBuildPieces(); RefreshPlayerAvailablePieces(); } protected override void OnDisabled() { List<Exception> list = new List<Exception>(); if (_pavedSettingSubscribed) { try { _pavedRoadWithoutStonecutter.SettingChanged -= OnPavedRoadSettingChanged; _pavedSettingSubscribed = false; } catch (Exception item) { list.Add(item); } } try { RestorePavedRoadStation(); } catch (Exception item2) { list.Add(item2); } _lastPieceTable = null; if (_activeModule == this) { _activeModule = null; } _surfaceTracker.Reset(); try { RefreshPlayerAvailablePieces(); } catch (Exception ex) { try { base.Log.LogWarning((object)("Could not refresh vanilla build-piece availability during cleanup: " + ex)); } catch { } } GetLastGroundColliderMethod = null; UpdateAvailablePiecesListMethod = null; if (list.Count != 0) { throw new AggregateException("Road feature cleanup was incomplete.", list); } } private static void PlayerSetPlaceModePrefix(PieceTable __0) { _activeModule?.RefreshPavedRoadStation(__0, reportMissingCandidate: true); } private static void PieceTableUpdateAvailablePrefix(PieceTable __instance) { _activeModule?.RefreshPavedRoadStation(__instance, reportMissingCandidate: false); } private static void PlayerHaveRequirementsPrefix(Piece __0) { _activeModule?.RefreshCurrentBuildPieces(); } private static void ZNetSceneOnDestroyPrefix() { RoadFeatureModule activeModule = _activeModule; if (activeModule != null) { activeModule.RestorePavedRoadStation(); activeModule._lastPieceTable = null; } } private void OnPavedRoadSettingChanged(object sender, EventArgs eventArgs) { if (_activeModule != this) { return; } try { if (_pavedRoadWithoutStonecutter.Value) { RefreshCurrentBuildPieces(); } else { RestorePavedRoadStation(); } RefreshPlayerAvailablePieces(); } catch (Exception ex) { RestorePavedRoadStation(); base.Log.LogError((object)("Paved Road recipe update failed; the vanilla station requirement was restored: " + ex)); } } private void RefreshCurrentBuildPieces() { if (!_pavedRoadWithoutStonecutter.Value) { RestorePavedRoadStation(); return; } Player localPlayer = Player.m_localPlayer; PieceTable val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetBuildTool() : null); if ((Object)(object)val != (Object)null) { RefreshPavedRoadStation(val, reportMissingCandidate: true); } else if ((Object)(object)_lastPieceTable != (Object)null) { RefreshPavedRoadStation(_lastPieceTable, reportMissingCandidate: false); } } private static void RefreshPlayerAvailablePieces() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { UpdateAvailablePiecesListMethod.Invoke(localPlayer, null); } } private void RefreshPavedRoadStation(PieceTable table, bool reportMissingCandidate) { if ((Object)(object)table == (Object)null) { return; } if (!_pavedRoadWithoutStonecutter.Value) { RestorePavedRoadStation(); return; } List<PieceTableEntryInspection> list = InspectPieceTable(table); List<PavedRoadCandidateShape> list2 = new List<PavedRoadCandidateShape>(list.Count); foreach (PieceTableEntryInspection item in list) { list2.Add(item.Shape); } PavedRoadCandidateSelection pavedRoadCandidateSelection = PavedRoadCandidateSelector.Select(list2); if (pavedRoadCandidateSelection.Outcome != PavedRoadDiscoveryOutcome.Unique) { RestorePavedRoadStation(); if (_lastPieceTable == table) { _lastPieceTable = null; } if (reportMissingCandidate && ShouldReportDiscoveryFailure(table, list)) { LogDiscoveryFailureOnce(table, pavedRoadCandidateSelection, list); } } else { PieceTableEntryInspection pieceTableEntryInspection = list[pavedRoadCandidateSelection.CandidateIndex]; _lastPieceTable = table; ApplyPavedRoadStationOverride(pieceTableEntryInspection.Piece, pieceTableEntryInspection.Describe()); } } private void ApplyPavedRoadStationOverride(Piece piece, string candidateDescription) { if (!_pavedRoadWithoutStonecutter.Value || (Object)(object)piece == (Object)null) { return; } switch (_stationOverride.Apply(piece)) { case StationOverrideApplyResult.Removed: if (!_loggedStationRemoved) { _loggedStationRemoved = true; base.Log.LogInfo((object)("Semantic Paved Road candidate found " + candidateDescription + "; station field removed, so no nearby stonecutter is required.")); } break; case StationOverrideApplyResult.AlreadyAbsent: if (!_loggedStationAlreadyAbsent) { _loggedStationAlreadyAbsent = true; base.Log.LogInfo((object)("Semantic Paved Road candidate found " + candidateDescription + "; its station field was already absent, so no change was needed.")); } break; case StationOverrideApplyResult.Conflict: if (!_loggedStationConflict) { _loggedStationConflict = true; base.Log.LogWarning((object)"Paved Road station field changed while Treadwell was active; the conflicting value was left untouched."); } break; case StationOverrideApplyResult.InvalidPiece: throw new InvalidOperationException("Semantic Paved Road discovery returned an invalid piece."); } } private List<PieceTableEntryInspection> InspectPieceTable(PieceTable table) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Invalid comparison between Unknown and I4 List<PieceTableEntryInspection> list = new List<PieceTableEntryInspection>(); if (table.m_pieces == null) { return list; } foreach (GameObject piece in table.m_pieces) { if ((Object)(object)piece == (Object)null) { continue; } Piece component = piece.GetComponent<Piece>(); Piece[] componentsInChildren = piece.GetComponentsInChildren<Piece>(true); TerrainModifier[] componentsInChildren2 = piece.GetComponentsInChildren<TerrainModifier>(true); Piece val = ((componentsInChildren.Length == 1 && componentsInChildren[0] == component) ? component : null); int num = 0; TerrainModifier[] array = componentsInChildren2; foreach (TerrainModifier val2 in array) { if ((Object)(object)val2 != (Object)null && (int)val2.m_paintType == 2) { num++; } } Requirement[] array2 = (((Object)(object)val != (Object)null) ? val.m_resources : null); int resourceRequirementCount = ((array2 != null) ? array2.Length : 0); int num2 = 0; int num3 = 0; if (array2 != null) { Requirement[] array3 = array2; foreach (Requirement val3 in array3) { if (val3 != null && !((Object)(object)val3.m_resItem == (Object)null) && val3.m_amount == 1) { num2++; if (IsExpectedStoneResource(val3.m_resItem)) { num3++; } } } } PavedRoadCandidateShape shape = new PavedRoadCandidateShape(componentsInChildren.Length, (Object)(object)val != (Object)null, componentsInChildren2.Length, num, (Object)(object)val != (Object)null && ((Object)(object)val.m_craftingStation != (Object)null || _stationOverride.IsAppliedTo(val)), resourceRequirementCount, num2, num3); list.Add(new PieceTableEntryInspection(piece, componentsInChildren, componentsInChildren2, shape)); } return list; } private static bool IsExpectedStoneResource(ItemDrop resource) { if ((Object)(object)resource == (Object)null || (Object)(object)((Component)resource).gameObject == (Object)null) { return false; } string name = ((Object)((Component)resource).gameObject).name; if (!string.Equals(name, "Stone", StringComparison.Ordinal)) { return string.Equals(name, "Stone(Clone)", StringComparison.Ordinal); } return true; } private static bool ShouldReportDiscoveryFailure(PieceTable table, List<PieceTableEntryInspection> entries) { if (IsLikelyHoePieceTable(table)) { return true; } foreach (PieceTableEntryInspection entry in entries) { if (entry.Shape.PavedTerrainModifierCount > 0) { return true; } } return false; } private static bool IsLikelyHoePieceTable(PieceTable table) { string text = (((Object)(object)table != (Object)null && (Object)(object)((Component)table).gameObject != (Object)null) ? ((Object)((Component)table).gameObject).name : null); if (text != null) { return text.IndexOf("hoe", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private void LogDiscoveryFailureOnce(PieceTable table, PavedRoadCandidateSelection selection, List<PieceTableEntryInspection> entries) { if (_loggedDiscoveryFailure) { return; } _loggedDiscoveryFailure = true; string text = ((selection.Outcome == PavedRoadDiscoveryOutcome.Ambiguous) ? ("ambiguous (" + selection.CandidateCount + " semantic matches)") : "no semantic match"); string text2 = (((Object)(object)table != (Object)null && (Object)(object)((Component)table).gameObject != (Object)null) ? SanitizeDiagnostic(((Object)((Component)table).gameObject).name) : "<unnamed>"); StringBuilder stringBuilder = new StringBuilder(); int num = Math.Min(entries.Count, 12); for (int i = 0; i < num; i++) { if (i > 0) { stringBuilder.Append("; "); } stringBuilder.Append(entries[i].Describe()); } if (entries.Count > num) { stringBuilder.Append("; +").Append(entries.Count - num).Append(" more"); } base.Log.LogWarning((object)("Paved Road semantic discovery was " + text + " in active table '" + text2 + "'; vanilla station requirements remain unchanged. Candidate shapes: " + stringBuilder)); } private static string SanitizeDiagnostic(string value) { if (string.IsNullOrEmpty(value)) { return "<none>"; } string text = value.Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' '); if (text.Length > 64) { return text.Substring(0, 64) + "..."; } return text; } private void RestorePavedRoadStation() { if (!_stationOverride.IsApplied) { return; } try { if (_stationOverride.Restore() == StationOverrideRestoreResult.Conflict) { base.Log.LogWarning((object)"Did not restore the Paved Road station because another runtime change replaced it."); } } catch (MissingReferenceException) { _stationOverride.Forget(); } } private static void GetRunSpeedFactorPostfix(Player __instance, ref float __result) { RoadFeatureModule activeModule = _activeModule; if (activeModule != null && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && ((Character)__instance).IsRunning()) { __result *= activeModule.CurrentTuning().SpeedMultiplier(activeModule.ResolveSurface(__instance)); } } private static void ModifyRunStaminaDrainPostfix(Character ___m_character, ref float drain) { RoadFeatureModule activeModule = _activeModule; Player val = (Player)(object)((___m_character is Player) ? ___m_character : null); if (activeModule != null && !((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { drain *= activeModule.CurrentTuning().StaminaMultiplier(activeModule.ResolveSurface(val)); } } private RoadTuning CurrentTuning() { return new RoadTuning(_dirtSpeed.Value, _dirtStamina.Value, _pavedSpeed.Value, _pavedStamina.Value); } private RoadSurface ResolveSurface(Player player) { //IL_007c: 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_0087: 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_0093: Unknown result type (might be due to invalid IL or missing references) double nowSeconds = Time.unscaledTime; if (!((Character)player).IsOnGround()) { return _surfaceTracker.Observe(TerrainSurface.NonTerrain, nowSeconds); } object? obj = GetLastGroundColliderMethod?.Invoke(player, null); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { return _surfaceTracker.Observe(TerrainSurface.NonTerrain, nowSeconds); } Heightmap val2 = val.GetComponent<Heightmap>() ?? val.GetComponentInParent<Heightmap>(); if ((Object)(object)val2 == (Object)null) { return _surfaceTracker.Observe(TerrainSurface.NonTerrain, nowSeconds); } Color paintMask = val2.GetPaintMask(((Component)player).transform.position); TerrainSurface observed = TerrainClassifier.Classify(paintMask.r, paintMask.g, paintMask.b); return _surfaceTracker.Observe(observed, nowSeconds); } } [BepInPlugin("com.jstack424.treadwell", "Treadwell", "0.1.2")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.jstack424.treadwell"; public const string PluginName = "Treadwell"; public const string PluginVersion = "0.1.2"; private FeatureHost _features; private void Awake() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown AcceptableValueRange<float> val = new AcceptableValueRange<float>(0f, 100f); ConfigEntry<bool> enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable mod", true, "Master switch for all Treadwell features."); ConfigEntry<bool> pavedRoadWithoutStonecutter = ((BaseUnityPlugin)this).Config.Bind<bool>("Road building", "Paved roads without stonecutter", true, "Allow the vanilla paved-road terrain piece to be placed without a nearby stonecutter. Stone cost and every other placement rule remain unchanged."); ConfigEntry<float> dirtSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Road bonuses", "Dirt sprint speed bonus (%)", 10f, new ConfigDescription("Extra sprint speed on vanilla dirt paths.", (AcceptableValueBase)(object)val, Array.Empty<object>())); ConfigEntry<float> dirtStamina = ((BaseUnityPlugin)this).Config.Bind<float>("Road bonuses", "Dirt sprint stamina reduction (%)", 10f, new ConfigDescription("Reduction to sprint stamina drain on vanilla dirt paths.", (AcceptableValueBase)(object)val, Array.Empty<object>())); ConfigEntry<float> pavedSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Road bonuses", "Paved sprint speed bonus (%)", 20f, new ConfigDescription("Extra sprint speed on vanilla paved roads.", (AcceptableValueBase)(object)val, Array.Empty<object>())); ConfigEntry<float> pavedStamina = ((BaseUnityPlugin)this).Config.Bind<float>("Road bonuses", "Paved sprint stamina reduction (%)", 20f, new ConfigDescription("Reduction to sprint stamina drain on vanilla paved roads.", (AcceptableValueBase)(object)val, Array.Empty<object>())); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Treadwell 0.1.2 (8583a261d57d056dff56c24aee6a3d8fad92ebfd) loading."); _features = new FeatureHost(new IFeatureModule[1] { new RoadFeatureModule(enabled, pavedRoadWithoutStonecutter, dirtSpeed, dirtStamina, pavedSpeed, pavedStamina, ((BaseUnityPlugin)this).Logger) }, ((BaseUnityPlugin)this).Logger); CompatibilityResult compatibilityResult = CompatibilityGate.Evaluate(_features); if (!compatibilityResult.IsCompatible) { ((BaseUnityPlugin)this).Logger.LogError((object)("Compatibility gate failed. Treadwell is fully disabled before any gameplay hooks were installed: " + compatibilityResult.Reason)); _features.Dispose(); _features = null; return; } try { _features.Start(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Compatibility gate passed: " + compatibilityResult.Reason + "; configured feature modules are enabled.")); } catch (Exception ex) { _features.Stop(); ((BaseUnityPlugin)this).Logger.LogError((object)("Treadwell disabled because feature installation failed: " + ex)); } } private void OnDisable() { _features?.Stop(); } private void OnDestroy() { _features?.Dispose(); _features = null; } } internal static class GeneratedBuildInfo { public const string Version = "0.1.2"; public const string Commit = "8583a261d57d056dff56c24aee6a3d8fad92ebfd"; } } namespace Treadwell.Core { public enum PavedRoadDiscoveryOutcome { None, Unique, Ambiguous } public sealed class PavedRoadCandidateShape { public int PieceComponentCount { get; } public bool HasRootPiece { get; } public int TerrainModifierCount { get; } public int PavedTerrainModifierCount { get; } public bool HasStationRequirement { get; } public int ResourceRequirementCount { get; } public int SingleUnitResourceRequirementCount { get; } public int SingleUnitStoneResourceRequirementCount { get; } public bool IsSemanticCandidate { get { if (PieceComponentCount == 1 && HasRootPiece && PavedTerrainModifierCount == 1 && HasStationRequirement && ResourceRequirementCount == 1 && SingleUnitResourceRequirementCount == 1) { return SingleUnitStoneResourceRequirementCount == 1; } return false; } } public PavedRoadCandidateShape(int pieceComponentCount, bool hasRootPiece, int terrainModifierCount, int pavedTerrainModifierCount, bool hasStationRequirement, int resourceRequirementCount, int singleUnitResourceRequirementCount, int singleUnitStoneResourceRequirementCount) { PieceComponentCount = pieceComponentCount; HasRootPiece = hasRootPiece; TerrainModifierCount = terrainModifierCount; PavedTerrainModifierCount = pavedTerrainModifierCount; HasStationRequirement = hasStationRequirement; ResourceRequirementCount = resourceRequirementCount; SingleUnitResourceRequirementCount = singleUnitResourceRequirementCount; SingleUnitStoneResourceRequirementCount = singleUnitStoneResourceRequirementCount; } } public sealed class PavedRoadCandidateSelection { public PavedRoadDiscoveryOutcome Outcome { get; } public int CandidateIndex { get; } public int CandidateCount { get; } internal PavedRoadCandidateSelection(PavedRoadDiscoveryOutcome outcome, int candidateIndex, int candidateCount) { Outcome = outcome; CandidateIndex = candidateIndex; CandidateCount = candidateCount; } } public static class PavedRoadCandidateSelector { public static PavedRoadCandidateSelection Select(IReadOnlyList<PavedRoadCandidateShape> shapes) { if (shapes == null) { throw new ArgumentNullException("shapes"); } int candidateIndex = -1; int num = 0; for (int i = 0; i < shapes.Count; i++) { PavedRoadCandidateShape pavedRoadCandidateShape = shapes[i]; if (pavedRoadCandidateShape != null && pavedRoadCandidateShape.IsSemanticCandidate) { candidateIndex = i; num++; } } return num switch { 0 => new PavedRoadCandidateSelection(PavedRoadDiscoveryOutcome.None, -1, 0), 1 => new PavedRoadCandidateSelection(PavedRoadDiscoveryOutcome.Unique, candidateIndex, 1), _ => new PavedRoadCandidateSelection(PavedRoadDiscoveryOutcome.Ambiguous, -1, num), }; } } public enum StationOverrideApplyResult { InvalidPiece, Removed, AlreadyAbsent, Conflict } public enum StationOverrideRestoreResult { NothingToRestore, Restored, AlreadyRestored, Conflict } public sealed class PavedRoadStationOverride<TPiece, TStation> where TPiece : class where TStation : class { private readonly Func<TPiece, TStation?> _getStation; private readonly Action<TPiece, TStation?> _setStation; private TPiece? _piece; private TStation? _originalStation; public bool IsApplied => _piece != null; public PavedRoadStationOverride(Func<TPiece, TStation?> getStation, Action<TPiece, TStation?> setStation) { _getStation = getStation ?? throw new ArgumentNullException("getStation"); _setStation = setStation ?? throw new ArgumentNullException("setStation"); } public bool IsAppliedTo(TPiece piece) { if (piece != null) { return _piece == piece; } return false; } public StationOverrideApplyResult Apply(TPiece piece) { if (piece == null) { return StationOverrideApplyResult.InvalidPiece; } TStation val = _getStation(piece); if (_piece == piece) { if (val == null) { return StationOverrideApplyResult.AlreadyAbsent; } if (val != _originalStation) { return StationOverrideApplyResult.Conflict; } _setStation(piece, null); return StationOverrideApplyResult.Removed; } if (val == null) { return StationOverrideApplyResult.AlreadyAbsent; } if (_piece != null) { Restore(); } _piece = piece; _originalStation = val; _setStation(piece, null); return StationOverrideApplyResult.Removed; } public StationOverrideRestoreResult Restore() { TPiece piece = _piece; TStation originalStation = _originalStation; if (piece == null) { return StationOverrideRestoreResult.NothingToRestore; } TStation val = _getStation(piece); if (val != null) { int result = ((val == originalStation) ? 2 : 3); Forget(); return (StationOverrideRestoreResult)result; } if (originalStation == null) { Forget(); return StationOverrideRestoreResult.Conflict; } _setStation(piece, originalStation); Forget(); return StationOverrideRestoreResult.Restored; } public void Forget() { _piece = null; _originalStation = null; } } public enum TerrainSurface { Natural, DirtPath, Cultivated, PavedRoad, NonTerrain } public enum RoadSurface { None, Dirt, Paved } public static class TerrainClassifier { public const float MinimumPaintStrength = 0.1f; public const float DominanceMargin = 0.02f; public static TerrainSurface Classify(float red, float green, float blue) { if (!IsFinite(red) || !IsFinite(green) || !IsFinite(blue)) { return TerrainSurface.Natural; } if (green >= 0.1f && green >= red - 0.02f && green >= blue - 0.02f) { return TerrainSurface.Cultivated; } if (blue >= 0.1f && blue > red + 0.02f && blue > green + 0.02f) { return TerrainSurface.PavedRoad; } if (red >= 0.1f && red > green + 0.02f && red > blue + 0.02f) { return TerrainSurface.DirtPath; } return TerrainSurface.Natural; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } public sealed class RoadTuning { public float DirtSpeedPercent { get; } public float DirtStaminaReductionPercent { get; } public float PavedSpeedPercent { get; } public float PavedStaminaReductionPercent { get; } public RoadTuning(float dirtSpeedPercent, float dirtStaminaReductionPercent, float pavedSpeedPercent, float pavedStaminaReductionPercent) { DirtSpeedPercent = ClampPercent(dirtSpeedPercent); DirtStaminaReductionPercent = ClampPercent(dirtStaminaReductionPercent); PavedSpeedPercent = ClampPercent(pavedSpeedPercent); PavedStaminaReductionPercent = ClampPercent(pavedStaminaReductionPercent); } public float SpeedMultiplier(RoadSurface surface) { return surface switch { RoadSurface.Dirt => 1f + DirtSpeedPercent / 100f, RoadSurface.Paved => 1f + PavedSpeedPercent / 100f, _ => 1f, }; } public float StaminaMultiplier(RoadSurface surface) { return surface switch { RoadSurface.Dirt => 1f - DirtStaminaReductionPercent / 100f, RoadSurface.Paved => 1f - PavedStaminaReductionPercent / 100f, _ => 1f, }; } public static float ClampPercent(float value) { if (float.IsNaN(value) || float.IsNegativeInfinity(value)) { return 0f; } if (float.IsPositiveInfinity(value)) { return 100f; } if (value < 0f) { return 0f; } if (!(value > 100f)) { return value; } return 100f; } } public sealed class RoadSurfaceTracker { private readonly double _naturalGapHoldSeconds; private RoadSurface _active; private double _lastRoadTime = double.NegativeInfinity; private double _lastObservationTime = double.NegativeInfinity; public RoadSurfaceTracker(double naturalGapHoldSeconds) { if (double.IsNaN(naturalGapHoldSeconds) || double.IsInfinity(naturalGapHoldSeconds) || naturalGapHoldSeconds < 0.0) { throw new ArgumentOutOfRangeException("naturalGapHoldSeconds"); } _naturalGapHoldSeconds = naturalGapHoldSeconds; } public RoadSurface Observe(TerrainSurface observed, double nowSeconds) { if (double.IsNaN(nowSeconds) || double.IsInfinity(nowSeconds) || nowSeconds < _lastObservationTime) { Reset(); return RoadSurface.None; } _lastObservationTime = nowSeconds; switch (observed) { case TerrainSurface.DirtPath: case TerrainSurface.PavedRoad: _active = ((observed == TerrainSurface.DirtPath) ? RoadSurface.Dirt : RoadSurface.Paved); _lastRoadTime = nowSeconds; return _active; case TerrainSurface.Cultivated: case TerrainSurface.NonTerrain: ResetAt(nowSeconds); return RoadSurface.None; default: if (_active != RoadSurface.None && nowSeconds - _lastRoadTime <= _naturalGapHoldSeconds) { return _active; } ResetAt(nowSeconds); return RoadSurface.None; } } public void Reset() { ResetAt(double.NegativeInfinity); } private void ResetAt(double observationTime) { _active = RoadSurface.None; _lastRoadTime = double.NegativeInfinity; _lastObservationTime = observationTime; } } public static class ExactRuntimeContract { public static MethodInfo[] FindMethods(Type type, string name, BindingFlags flags, Type returnType, IReadOnlyList<Type> parameterTypes, Func<MethodInfo, bool>? additionalCheck = null) { return (from method in type.GetMethods(flags) where string.Equals(method.Name, name, StringComparison.Ordinal) where method.ReturnType == returnType where ParametersEqual(method.GetParameters(), parameterTypes) where additionalCheck == null || additionalCheck(method) select method).ToArray(); } public static ConstructorInfo[] FindConstructors(Type type, BindingFlags flags, IReadOnlyList<Type> parameterTypes) { return (from constructor in type.GetConstructors(flags) where ParametersEqual(constructor.GetParameters(), parameterTypes) select constructor).ToArray(); } public static FieldInfo[] FindFields(Type type, string name, BindingFlags flags, Type fieldType) { return (from field in type.GetFields(flags) where string.Equals(field.Name, name, StringComparison.Ordinal) where field.FieldType == fieldType select field).ToArray(); } public static PropertyInfo[] FindProperties(Type type, string name, BindingFlags flags, Type propertyType) { return (from property in type.GetProperties(flags) where string.Equals(property.Name, name, StringComparison.Ordinal) where property.PropertyType == propertyType select property).ToArray(); } private static bool ParametersEqual(ParameterInfo[] observed, IReadOnlyList<Type> expected) { if (observed.Length != expected.Count) { return false; } for (int i = 0; i < observed.Length; i++) { if (observed[i].ParameterType != expected[i]) { return false; } } return true; } } public static class TransactionalInstall { public static void Run(Action install, params Action[] rollbackSteps) { if (install == null) { throw new ArgumentNullException("install"); } if (rollbackSteps == null) { throw new ArgumentNullException("rollbackSteps"); } try { install(); } catch (Exception item) { List<Exception> list = new List<Exception> { item }; foreach (Action action in rollbackSteps) { if (action != null) { try { action(); } catch (Exception item2) { list.Add(item2); } } } if (list.Count > 1) { throw new AggregateException("Installation failed and rollback was incomplete.", list); } throw; } } } }