Decompiled source of Procedural Roads v1.1.0
ProceduralRoads.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ProceduralRoads")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("warpalicious")] [assembly: AssemblyProduct("ProceduralRoads")] [assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("E0E2F92E-557C-4A05-9D89-AA92A0BD75C4")] [assembly: AssemblyFileVersion("1.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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; } } } namespace ProceduralRoads { public class RoadPointDebugMarker : MonoBehaviour, Interactable, Hoverable { public RoadSpatialGrid.RoadPointDebugInfo DebugInfo; public Vector2 RoadPointPosition; public float RoadPointHeight; private static ManualLogSource Log => ProceduralRoadsPlugin.ProceduralRoadsLogger; public string GetHoverName() { return "Road Point Debug"; } public string GetHoverText() { float num = DebugInfo.SmoothedHeight - DebugInfo.OriginalHeight; string arg = ((num >= 0f) ? $"+{num:F2}m" : $"{num:F2}m"); return $"[<color=yellow><b>$KEY_Use</b></color>] Inspect point {DebugInfo.PointIndex}/{DebugInfo.TotalPoints} ({arg})"; } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold) { return false; } LogDebugInfo(); return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } private void LogDebugInfo() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("=== Road Point Debug ==="); stringBuilder.AppendLine($"Position: ({RoadPointPosition.x:F1}, {RoadPointPosition.y:F1})"); stringBuilder.AppendLine($"Index: {DebugInfo.PointIndex} of {DebugInfo.TotalPoints} points in road"); stringBuilder.AppendLine(); stringBuilder.AppendLine($"Terrain Height (blended): {DebugInfo.OriginalHeight:F2}m"); stringBuilder.AppendLine($"Smoothed Road Height: {DebugInfo.SmoothedHeight:F2}m"); float num = DebugInfo.SmoothedHeight - DebugInfo.OriginalHeight; string arg = ((num >= 0f) ? "road above terrain" : "road below terrain"); stringBuilder.AppendLine($"Delta: {num:F2}m ({arg})"); stringBuilder.AppendLine(); int num2 = 20; int num3 = DebugInfo.PointIndex - num2; int num4 = DebugInfo.PointIndex + num2; stringBuilder.AppendLine("Smoothing Window:"); stringBuilder.AppendLine($" Requested: {41} points (idx {num3} to {num4})"); string text = ((DebugInfo.ActualWindowSize >= 41) ? "[FULL WINDOW]" : ((DebugInfo.WindowStart != 0) ? "[TRUNCATED - at path end]" : "[TRUNCATED - at path start]")); stringBuilder.AppendLine($" Actual: {DebugInfo.ActualWindowSize} points (idx {DebugInfo.WindowStart} to {DebugInfo.WindowEnd}) {text}"); stringBuilder.AppendLine(); if (DebugInfo.WindowHeights != null && DebugInfo.WindowHeights.Length != 0) { stringBuilder.AppendLine("Heights in Window:"); int num5 = Mathf.Min(5, DebugInfo.WindowHeights.Length); StringBuilder stringBuilder2 = new StringBuilder(" First: "); for (int i = 0; i < num5; i++) { stringBuilder2.Append($"{DebugInfo.WindowHeights[i]:F1}m "); } stringBuilder.AppendLine(stringBuilder2.ToString()); if (DebugInfo.WindowHeights.Length > num5 * 2) { stringBuilder.AppendLine(" ..."); StringBuilder stringBuilder3 = new StringBuilder(" Last: "); for (int j = DebugInfo.WindowHeights.Length - num5; j < DebugInfo.WindowHeights.Length; j++) { stringBuilder3.Append($"{DebugInfo.WindowHeights[j]:F1}m "); } stringBuilder.AppendLine(stringBuilder3.ToString()); } } stringBuilder.AppendLine(); if (WorldGenerator.instance != null) { float height = WorldGenerator.instance.GetHeight(RoadPointPosition.x, RoadPointPosition.y); float blendedHeight = BiomeBlendedHeight.GetBlendedHeight(RoadPointPosition.x, RoadPointPosition.y, WorldGenerator.instance); stringBuilder.AppendLine("Height Comparison:"); stringBuilder.AppendLine($" Raw WorldGen height: {height:F2}m"); stringBuilder.AppendLine($" Biome-blended height: {blendedHeight:F2}m"); stringBuilder.AppendLine($" Blend difference: {blendedHeight - height:F2}m"); stringBuilder.AppendLine($" Road target: {DebugInfo.SmoothedHeight:F2}m"); } string text2 = stringBuilder.ToString(); Log.LogDebug((object)text2); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, $"Road point {DebugInfo.PointIndex}: delta={num:F2}m (see console)", 0, (Sprite)null); } } } public static class ZoneSystem_Patch { [HarmonyPatch(typeof(ZoneSystem), "Start")] public static class ZoneSystem_Start_Patch { [HarmonyPostfix] public static void Postfix(ZoneSystem __instance) { RoadNetworkGenerator.Initialize(); __instance.GenerateLocationsCompleted += OnLocationsGenerated; ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)"Subscribed to GenerateLocationsCompleted event"); } } [HarmonyPatch(typeof(ZoneSystem), "PlaceVegetation")] public static class ZoneSystem_PlaceVegetation_Patch { [HarmonyPrefix] public static void Prefix(Vector2i zoneID, List<ClearArea> clearAreas) { //IL_000d: 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_0023: Unknown result type (might be due to invalid IL or missing references) if (RoadNetworkGenerator.RoadsGenerated) { if (!s_roadClearAreasCache.TryGetValue(zoneID, out List<ClearArea> value)) { value = CreateRoadClearAreas(zoneID); s_roadClearAreasCache[zoneID] = value; } clearAreas.AddRange(value); } } } [HarmonyPatch(typeof(ZoneSystem), "SpawnZone")] public static class ZoneSystem_SpawnZone_Patch { [HarmonyPostfix] public static void Postfix(ZoneSystem __instance, Vector2i zoneID, SpawnMode mode, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if ((int)mode == 1) { return; } if (RoadNetworkGenerator.IsLocationsReady && !RoadNetworkGenerator.RoadsAvailable) { if (TryLoadRoadDataFromZDO(zoneID)) { RoadNetworkGenerator.MarkRoadsLoadedFromZDO(); ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)"Roads found in ZDO, loading from persistence"); } else { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)"No roads in ZDO, generating (deferred)..."); RoadNetworkGenerator.GenerateRoads(); RoadNetworkGenerator.SaveRoadMetadata(); } } if (__result && RoadNetworkGenerator.RoadsGenerated) { List<RoadSpatialGrid.RoadPoint> roadPointsInZone = RoadSpatialGrid.GetRoadPointsInZone(zoneID); if (roadPointsInZone.Count > 0) { s_zoneTimer.Restart(); ApplyRoadTerrainModsAndPersist(zoneID, roadPointsInZone); s_zoneTimer.Stop(); double totalMilliseconds = s_zoneTimer.Elapsed.TotalMilliseconds; s_timedZoneCount++; s_totalZoneTimeMs += totalMilliseconds; if (totalMilliseconds > s_maxZoneTimeMs) { s_maxZoneTimeMs = totalMilliseconds; } ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Zone {zoneID}: {roadPointsInZone.Count} road points, {totalMilliseconds:F2}ms"); if (s_timedZoneCount % 10 == 0) { double num = s_totalZoneTimeMs / (double)s_timedZoneCount; ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"[PERF] Road zones: {s_timedZoneCount} processed, {s_skippedZoneCount} loaded from ZDO, avg={num:F2}ms, max={s_maxZoneTimeMs:F2}ms"); } } } else if (RoadNetworkGenerator.RoadsLoadedFromZDO) { TryLoadRoadDataFromZDO(zoneID); } } } private struct TerrainContext { public Heightmap Heightmap; public TerrainComp TerrainComp; public Vector3 HeightmapPosition; public int GridSize; public float VertexSpacing; } private struct ModificationStats { public int VerticesModified; public int VerticesChecked; public HashSet<Vector2i> PaintedCells; } private struct BlendResult { public float TargetHeight; public float MaxBlend; public int InfluencingPoints; } [HarmonyPatch(typeof(ZoneSystem), "OnDestroy")] public static class ZoneSystem_OnDestroy_Patch { [HarmonyPrefix] public static void Prefix(ZoneSystem __instance) { if (s_timedZoneCount > 0 || s_skippedZoneCount > 0) { double num = ((s_timedZoneCount > 0) ? (s_totalZoneTimeMs / (double)s_timedZoneCount) : 0.0); ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)($"[PERF FINAL] Road zones: {s_timedZoneCount} processed, {s_skippedZoneCount} skipped, " + $"avg={num:F2}ms, max={s_maxZoneTimeMs:F2}ms, total={s_totalZoneTimeMs:F0}ms")); } __instance.GenerateLocationsCompleted -= OnLocationsGenerated; RoadNetworkGenerator.Reset(); s_roadClearAreasCache.Clear(); s_coordLogCount = 0; s_timedZoneCount = 0; s_totalZoneTimeMs = 0.0; s_maxZoneTimeMs = 0.0; s_skippedZoneCount = 0; ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)"Road data cleared on world unload"); } } [HarmonyPatch(typeof(Game), "SpawnPlayer")] public static class Game_SpawnPlayer_Patch { [HarmonyPostfix] public static void Postfix(Vector3 spawnPoint) { //IL_0019: 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_0035: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (!RoadNetworkGenerator.IsLocationsReady || RoadNetworkGenerator.RoadsAvailable) { return; } ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Player spawning at {spawnPoint}, loading road data from ZDOs..."); RoadNetworkGenerator.MarkRoadsLoadedFromZDO(); RoadNetworkGenerator.TryLoadRoadMetadata(); Vector2i zone = ZoneSystem.GetZone(spawnPoint); int num = 0; for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) { if (TryLoadRoadDataFromZDO(new Vector2i(zone.x + i, zone.y + j))) { num++; } } } ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Loaded road data from {num} zones near player"); } } private static readonly Dictionary<Vector2i, List<ClearArea>> s_roadClearAreasCache = new Dictionary<Vector2i, List<ClearArea>>(); private static int s_coordLogCount = 0; private static readonly Stopwatch s_zoneTimer = new Stopwatch(); private static int s_timedZoneCount = 0; private static double s_totalZoneTimeMs = 0.0; private static double s_maxZoneTimeMs = 0.0; private const int TimingLogInterval = 10; private static int s_skippedZoneCount = 0; private static void OnLocationsGenerated() { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)"Location generation complete..."); RoadNetworkGenerator.MarkLocationsReady(); s_roadClearAreasCache.Clear(); bool flag = WorldGenerator.instance != null; ZoneSystem instance = ZoneSystem.instance; bool flag2 = instance != null && instance.GetLocationList()?.Count > 0; if (flag && flag2) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"WorldGenerator and locations available ({ZoneSystem.instance.GetLocationList().Count} locations), generating roads now..."); RoadNetworkGenerator.GenerateRoads(); RoadNetworkGenerator.SaveRoadMetadata(); } else { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Deferring road generation (WorldGen={flag}, Locations={flag2})..."); } } private static List<ClearArea> CreateRoadClearAreas(Vector2i zoneID) { //IL_0006: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown List<ClearArea> list = new List<ClearArea>(); List<RoadSpatialGrid.RoadPoint> roadPointsInZone = RoadSpatialGrid.GetRoadPointsInZone(zoneID); if (roadPointsInZone.Count == 0) { return list; } HashSet<Vector2i> hashSet = new HashSet<Vector2i>(); Vector2i val = default(Vector2i); Vector3 val2 = default(Vector3); foreach (RoadSpatialGrid.RoadPoint item in roadPointsInZone) { ((Vector2i)(ref val))..ctor(Mathf.RoundToInt(item.p.x / 4f), Mathf.RoundToInt(item.p.y / 4f)); if (!hashSet.Contains(val)) { hashSet.Add(val); ((Vector3)(ref val2))..ctor((float)val.x * 4f, 0f, (float)val.y * 4f); float num = item.w * 0.6f; list.Add(new ClearArea(val2, num)); } } return list; } private static bool TryLoadRoadDataFromZDO(Vector2i zoneID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_0083: Unknown result type (might be due to invalid IL or missing references) Heightmap val = Heightmap.FindHeightmap(ZoneSystem.GetZonePos(zoneID)); if ((Object)(object)val == (Object)null) { return false; } TerrainComp andCreateTerrainCompiler = val.GetAndCreateTerrainCompiler(); if ((Object)(object)andCreateTerrainCompiler == (Object)null || !andCreateTerrainCompiler.m_nview.IsValid()) { return false; } byte[] byteArray = andCreateTerrainCompiler.m_nview.GetZDO().GetByteArray(RoadSpatialGrid.RoadDataHash, (byte[])null); if (byteArray != null && byteArray.Length != 0) { RoadSpatialGrid.DeserializeZoneRoadPoints(zoneID, byteArray); s_skippedZoneCount++; if (s_skippedZoneCount <= 5 || s_skippedZoneCount % 20 == 0) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Zone {zoneID}: loaded {byteArray.Length} bytes from ZDO, total loaded: {s_skippedZoneCount}"); } return true; } return false; } private static void ApplyRoadTerrainModsAndPersist(Vector2i zoneID, List<RoadSpatialGrid.RoadPoint> roadPoints) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) TerrainContext? terrainContext = GetTerrainContext(zoneID); if (!terrainContext.HasValue) { return; } if (terrainContext.Value.TerrainComp.m_nview.IsValid() && terrainContext.Value.TerrainComp.m_nview.IsOwner()) { byte[] array = RoadSpatialGrid.SerializeZoneRoadPoints(zoneID); if (array != null) { terrainContext.Value.TerrainComp.m_nview.GetZDO().Set(RoadSpatialGrid.RoadDataHash, array); } } ModificationStats stats = ModifyVertexHeights(zoneID, roadPoints, terrainContext.Value); ApplyRoadPaint(roadPoints, terrainContext.Value.TerrainComp, stats.PaintedCells); FinalizeTerrainMods(zoneID, roadPoints.Count, stats, terrainContext.Value); } public static void ApplyRoadTerrainModsPublic(Vector2i zoneID, List<RoadSpatialGrid.RoadPoint> roadPoints, Heightmap heightmap, TerrainComp terrainComp) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if (roadPoints != null && roadPoints.Count != 0) { int num = terrainComp.m_width + 1; if (terrainComp.m_levelDelta == null || terrainComp.m_levelDelta.Length < num * num) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Zone {zoneID}: TerrainComp arrays not initialized"); return; } TerrainContext terrainContext = default(TerrainContext); terrainContext.Heightmap = heightmap; terrainContext.TerrainComp = terrainComp; terrainContext.HeightmapPosition = ((Component)heightmap).transform.position; terrainContext.GridSize = num; terrainContext.VertexSpacing = 64f / (float)terrainComp.m_width; TerrainContext context = terrainContext; ModificationStats stats = ModifyVertexHeights(zoneID, roadPoints, context); ApplyRoadPaint(roadPoints, context.TerrainComp, stats.PaintedCells); FinalizeTerrainMods(zoneID, roadPoints.Count, stats, context); } } private static TerrainContext? GetTerrainContext(Vector2i zoneID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_0053: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) Heightmap val = Heightmap.FindHeightmap(ZoneSystem.GetZonePos(zoneID)); if ((Object)(object)val == (Object)null) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"No heightmap found for zone {zoneID}"); return null; } TerrainComp andCreateTerrainCompiler = val.GetAndCreateTerrainCompiler(); if ((Object)(object)andCreateTerrainCompiler == (Object)null) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Could not get TerrainComp for zone {zoneID}"); return null; } if (!andCreateTerrainCompiler.m_nview.IsOwner()) { return null; } int num = andCreateTerrainCompiler.m_width + 1; if (andCreateTerrainCompiler.m_levelDelta == null || andCreateTerrainCompiler.m_levelDelta.Length < num * num) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Zone {zoneID}: TerrainComp arrays not initialized"); return null; } TerrainContext value = default(TerrainContext); value.Heightmap = val; value.TerrainComp = andCreateTerrainCompiler; value.HeightmapPosition = ((Component)val).transform.position; value.GridSize = num; value.VertexSpacing = 64f / (float)andCreateTerrainCompiler.m_width; return value; } private static ModificationStats ModifyVertexHeights(Vector2i zoneID, List<RoadSpatialGrid.RoadPoint> roadPoints, TerrainContext context) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) ModificationStats modificationStats = default(ModificationStats); modificationStats.PaintedCells = new HashSet<Vector2i>(); ModificationStats result = modificationStats; LogCoordinateDebug(zoneID, roadPoints, context); Vector3 val = default(Vector3); Vector2 vertexPos = default(Vector2); for (int i = 0; i < context.GridSize; i++) { for (int j = 0; j < context.GridSize; j++) { result.VerticesChecked++; ((Vector3)(ref val))..ctor(context.HeightmapPosition.x + ((float)j - (float)context.TerrainComp.m_width / 2f) * context.VertexSpacing, 0f, context.HeightmapPosition.z + ((float)i - (float)context.TerrainComp.m_width / 2f) * context.VertexSpacing); ((Vector2)(ref vertexPos))..ctor(val.x, val.z); BlendResult blendResult = CalculateBlendedHeight(roadPoints, vertexPos); if (blendResult.InfluencingPoints == 0) { continue; } float blendedHeight = BiomeBlendedHeight.GetBlendedHeight(val.x, val.z, WorldGenerator.instance); float num = Mathf.Clamp(Mathf.Lerp(blendedHeight, blendResult.TargetHeight, blendResult.MaxBlend) - blendedHeight, -8f, 8f); if (Mathf.Abs(num) > 0.01f || blendResult.MaxBlend > 0.5f) { int num2 = i * context.GridSize + j; context.TerrainComp.m_levelDelta[num2] = num; context.TerrainComp.m_smoothDelta[num2] = 0f; context.TerrainComp.m_modifiedHeight[num2] = true; result.VerticesModified++; if (result.VerticesModified <= 3) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)($"[VERTEX] Zone {zoneID} v[{j},{i}]: pos=({val.x:F1},{val.z:F1}), " + $"base={blendedHeight:F2}m, target={blendResult.TargetHeight:F2}m, blend={blendResult.MaxBlend:F2}, delta={num:F2}m")); } } } } return result; } private static BlendResult CalculateBlendedHeight(List<RoadSpatialGrid.RoadPoint> roadPoints, Vector2 vertexPos) { //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_0032: 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) float num = 0f; float num2 = 0f; float num3 = 0f; int num4 = 0; foreach (RoadSpatialGrid.RoadPoint roadPoint in roadPoints) { Vector2 val = roadPoint.p - vertexPos; float sqrMagnitude = ((Vector2)(ref val)).sqrMagnitude; float num5 = roadPoint.w * 0.5f + 2f; float num6 = num5 * num5; if (sqrMagnitude < num6) { float num7 = Mathf.Sqrt(sqrMagnitude) / num5; float num8 = 1f - Mathf.SmoothStep(0f, 1f, num7); float num9 = num8 * num8; num += roadPoint.h * num9; num2 += num9; num4++; if (num8 > num3) { num3 = num8; } } } BlendResult result = default(BlendResult); result.TargetHeight = ((num4 > 0) ? (num / num2) : 0f); result.MaxBlend = num3; result.InfluencingPoints = num4; return result; } private static void ApplyRoadPaint(List<RoadSpatialGrid.RoadPoint> roadPoints, TerrainComp terrainComp, HashSet<Vector2i> paintedCells) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_0090: 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_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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: 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_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) Heightmap hmap = terrainComp.m_hmap; if ((Object)(object)hmap == (Object)null) { return; } int num = terrainComp.m_width + 1; Vector3 position = ((Component)hmap).transform.position; float scale = hmap.m_scale; Vector2i item = default(Vector2i); foreach (RoadSpatialGrid.RoadPoint roadPoint in roadPoints) { ((Vector2i)(ref item))..ctor(Mathf.RoundToInt(roadPoint.p.x / 1.5f), Mathf.RoundToInt(roadPoint.p.y / 1.5f)); if (paintedCells.Contains(item)) { continue; } paintedCells.Add(item); Vector3 val = new Vector3(roadPoint.p.x - 0.5f, 0f, roadPoint.p.y - 0.5f) - position; int num2 = (terrainComp.m_width + 1) / 2; int num3 = Mathf.FloorToInt(val.x / scale + 0.5f) + num2; int num4 = Mathf.FloorToInt(val.z / scale + 0.5f) + num2; float num5 = roadPoint.w * 0.5f / scale; int num6 = Mathf.CeilToInt(num5); for (int i = -num6; i <= num6; i++) { for (int j = -num6; j <= num6; j++) { int num7 = num3 + j; int num8 = num4 + i; if (num7 >= 0 && num8 >= 0 && num7 < num && num8 < num) { float num9 = Mathf.Sqrt((float)(j * j + i * i)); if (!(num9 > num5)) { float num10 = 1f - Mathf.Clamp01(num9 / num5); num10 = Mathf.Pow(num10, 0.1f); int num11 = num8 * num + num7; Color val2 = terrainComp.m_paintMask[num11]; float a = val2.a; Color val3 = Color.Lerp(val2, Heightmap.m_paintMaskPaved, num10); val3.a = a; terrainComp.m_modifiedPaint[num11] = true; terrainComp.m_paintMask[num11] = val3; } } } } } } private static void FinalizeTerrainMods(Vector2i zoneID, int roadPointCount, ModificationStats stats, TerrainContext context) { //IL_0042: 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) int count = stats.PaintedCells.Count; if (stats.VerticesModified > 0 || count > 0) { context.TerrainComp.Save(); context.Heightmap.Poke(true); ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)$"Zone {zoneID}: {stats.VerticesModified}/{stats.VerticesChecked} vertices modified, {count} paint cells"); } else if (roadPointCount > 0) { ProceduralRoadsPlugin.ProceduralRoadsLogger.LogWarning((object)$"Zone {zoneID}: {roadPointCount} road points but 0 vertices matched! Coordinate mismatch?"); } } private static void LogCoordinateDebug(Vector2i zoneID, List<RoadSpatialGrid.RoadPoint> roadPoints, TerrainContext context) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_00b4: 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) if (s_coordLogCount < 3) { s_coordLogCount++; float num = (float)context.TerrainComp.m_width / 2f * context.VertexSpacing; RoadSpatialGrid.RoadPoint roadPoint = ((roadPoints.Count > 0) ? roadPoints[0] : default(RoadSpatialGrid.RoadPoint)); ProceduralRoadsPlugin.ProceduralRoadsLogger.LogDebug((object)($"[COORD DEBUG] Zone {zoneID}: hmPos=({context.HeightmapPosition.x:F1},{context.HeightmapPosition.z:F1}), " + $"vertices cover X[{context.HeightmapPosition.x - num:F1},{context.HeightmapPosition.x + num:F1}], " + $"first road point=({roadPoint.p.x:F1},{roadPoint.p.y:F1}), width={roadPoint.w:F1}m")); } } } [BepInPlugin("warpalicious.ProceduralRoads", "ProceduralRoads", "1.1.0")] public class ProceduralRoadsPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } internal const string ModName = "ProceduralRoads"; internal const string ModVersion = "1.1.0"; internal const string Author = "warpalicious"; private const string ModGUID = "warpalicious.ProceduralRoads"; private static string ConfigFileName = "warpalicious.ProceduralRoads.cfg"; private static string ConfigFileFullPath; internal static string ConnectionError; private readonly Harmony _harmony = new Harmony("warpalicious.ProceduralRoads"); public static readonly ManualLogSource ProceduralRoadsLogger; public Texture2D tex; public static ConfigEntry<float> RoadWidth; public static ConfigEntry<string> CustomLocations; public static ConfigEntry<int> IslandRoadPercentage; public void Awake() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; RoadWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Roads", "RoadWidth", 4f, new ConfigDescription("Width of generated roads in meters", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 10f), Array.Empty<object>())); IslandRoadPercentage = ((BaseUnityPlugin)this).Config.Bind<int>("Roads", "IslandRoadPercentage", 50, new ConfigDescription("Percentage of islands that will have roads generated (0-100). Islands are selected by size (largest first).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); CustomLocations = ((BaseUnityPlugin)this).Config.Bind<string>("Locations", "CustomLocations", "", "Comma-separated list of location names to include in road generation. Use this for locations added by Expand World Data or other mods. Example: Runestone_Boars,Runestone_Greydwarfs,MerchantCamp"); ApplyConfiguration(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SetupWatcher(); ProceduralRoadsLogger.LogInfo((object)"ProceduralRoads v1.1.0 loaded - Procedural roads enabled"); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; ((BaseUnityPlugin)this).Config.Save(); } } private static void ApplyConfiguration() { RoadNetworkGenerator.RoadWidth = RoadWidth.Value; RoadNetworkGenerator.IslandRoadPercentage = IslandRoadPercentage.Value; } public static HashSet<string> GetConfigLocationNames() { HashSet<string> hashSet = new HashSet<string>(); if (string.IsNullOrWhiteSpace(CustomLocations.Value)) { return hashSet; } string[] array = CustomLocations.Value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrEmpty(text)) { hashSet.Add(text); } } return hashSet; } private void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void SetupWatcher() { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(ConfigFileFullPath)) { return; } try { ProceduralRoadsLogger.LogDebug((object)"ReadConfigValues called"); ((BaseUnityPlugin)this).Config.Reload(); ApplyConfiguration(); } catch { ProceduralRoadsLogger.LogError((object)("There was an issue loading your " + ConfigFileName)); ProceduralRoadsLogger.LogError((object)"Please check your config entries for spelling and format!"); } } static ProceduralRoadsPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ConnectionError = ""; ProceduralRoadsLogger = Logger.CreateLogSource("ProceduralRoads"); RoadWidth = null; CustomLocations = null; IslandRoadPercentage = null; } } public static class KeyboardExtensions { public static bool IsKeyDown(this KeyboardShortcut shortcut) { //IL_0002: 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) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } public static bool IsKeyHeld(this KeyboardShortcut shortcut) { //IL_0002: 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) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } } public static class RoadConstants { public const float ZoneSize = 64f; public const float HalfZoneSize = 32f; public const float SeaLevel = 30f; public const float DeepWaterHeight = 28f; public const float ShallowWaterHeight = 30.5f; public const float TerrainDeltaMin = -8f; public const float TerrainDeltaMax = 8f; public const float PathfindingCellSize = 8f; public const int PathfindingMaxIterations = 100000; public const float TerrainVarianceSampleRadius = 16f; public const int TerrainVarianceSampleCount = 8; public const float MountainSlopeThreshold = 0.4f; public const float RiverImpassableThreshold = 0.5f; public const float DefaultBaseCost = 1f; public const float DefaultSlopeMultiplier = 10f; public const float DefaultRiverPenalty = 100000f; public const float DefaultWaterPenalty = 100000f; public const float DefaultSteepSlopePenalty = 2000f; public const float DefaultSteepSlopeThreshold = 0.6f; public const float DefaultTerrainVariancePenalty = 1000f; public const float DefaultTerrainVarianceThreshold = 5f; public const float SpatialGridSize = 64f; public const float DefaultRoadWidth = 4f; public const float EdgeFalloffStart = 0.6f; public const int HeightSmoothingWindow = 41; public const float OverlapThreshold = 0.3f; public const float OverlapSearchRadiusMultiplier = 0.6f; public const float OverlapBlendRadiusMultiplier = 0.8f; public const float TerrainBlendMargin = 2f; public const float PaintDedupeInterval = 1.5f; public const float MinHeightDeltaThreshold = 0.01f; public const float MinBlendForModification = 0.5f; public const float VegetationClearMultiplier = 0.6f; public const float VegetationClearSampleInterval = 4f; public const int MaxCoordDebugLogs = 3; public const int MaxVertexModificationLogs = 3; } public static class RoadNetworkGenerator { public struct LocationData { public Vector3 SpawnPoint; public float SpawnRadius; public List<(string name, Vector3 position, float radius)> BossLocations; public List<(string name, Vector3 position, float radius)> AllLocations; } private static readonly HashSet<string> BossLocationNames = new HashSet<string> { "Eikthyrnir", "GDKing", "Bonemass", "Dragonqueen", "GoblinKing", "SeekerQueen" }; private static readonly Dictionary<string, int> LocationPriorities = new Dictionary<string, int> { { "Eikthyrnir", 100 }, { "GDKing", 100 }, { "Bonemass", 100 }, { "Dragonqueen", 100 }, { "GoblinKing", 100 }, { "SeekerQueen", 100 }, { "Crypt4", 80 }, { "SunkenCrypt4", 80 }, { "MountainCave02", 80 }, { "TrollCave02", 40 }, { "Crypt3", 75 }, { "Mistlands_DvergrTownEntrance1", 75 }, { "Mistlands_DvergrTownEntrance2", 75 }, { "Mistlands_Harbour1", 70 }, { "WoodVillage1", 60 }, { "WoodFarm1", 55 }, { "Mistlands_GuardTower1_new", 50 }, { "Mistlands_GuardTower2_new", 50 }, { "Mistlands_GuardTower3_new", 50 }, { "Mistlands_Lighthouse1_new", 50 }, { "Mistlands_Excavation1", 45 }, { "Mistlands_Excavation2", 45 }, { "Mistlands_Excavation3", 45 }, { "StoneTower1", 40 }, { "StoneTower3", 40 }, { "Mistlands_GuardTower1_ruined_new", 30 }, { "Mistlands_GuardTower3_ruined_new", 30 }, { "StoneTowerRuins03", 30 }, { "StoneTowerRuins04", 30 }, { "StoneTowerRuins05", 30 }, { "StoneTowerRuins07", 30 }, { "StoneTowerRuins08", 30 }, { "StoneTowerRuins09", 30 }, { "StoneTowerRuins10", 30 }, { "StoneHenge1", 25 }, { "StoneHenge2", 25 }, { "StoneHenge3", 25 }, { "SwampHut5", 25 }, { "SwampRuin1", 25 }, { "SwampRuin2", 25 } }; private const int DefaultPriority = 20; private const int MinLocationsPerIsland = 2; private const int MaxLocationsPerIsland = 12; private const float AreaPerLocation = 2000000f; private static readonly HashSet<string> RegisteredLocationNames = new HashSet<string>(); public static float RoadWidth = 4f; public static int IslandRoadPercentage = 50; private static bool m_roadsGenerated = false; private static bool m_locationsReady = false; private static bool m_roadsLoadedFromZDO = false; private static RoadPathfinder? m_pathfinder; private static int m_roadsGeneratedCount = 0; private static List<(Vector2 position, string label)> m_roadStartPoints = new List<(Vector2, string)>(); private const string MetadataPrefabName = "ProceduralRoads_Metadata"; private static readonly int MetadataPrefabHash = StringExtensionMethods.GetStableHashCode("ProceduralRoads_Metadata"); private static readonly int RoadStartPointsHash = StringExtensionMethods.GetStableHashCode("ProceduralRoads_StartPoints"); private static ManualLogSource Log => ProceduralRoadsPlugin.ProceduralRoadsLogger; public static bool RoadsGenerated => m_roadsGenerated; public static bool IsLocationsReady => m_locationsReady; public static bool RoadsLoadedFromZDO => m_roadsLoadedFromZDO; public static bool RoadsAvailable { get { if (!m_roadsGenerated) { return m_roadsLoadedFromZDO; } return true; } } public static void RegisterLocation(string locationName) { if (!string.IsNullOrWhiteSpace(locationName)) { string text = locationName.Trim(); if (RegisteredLocationNames.Add(text)) { Log.LogDebug((object)("Registered location for roads: " + text)); } } } public static void UnregisterLocation(string locationName) { if (!string.IsNullOrWhiteSpace(locationName)) { string text = locationName.Trim(); if (RegisteredLocationNames.Remove(text)) { Log.LogDebug((object)("Unregistered location from roads: " + text)); } } } public static IReadOnlyCollection<string> GetRegisteredLocations() { return RegisteredLocationNames; } public static IReadOnlyList<(Vector2 position, string label)> GetRoadStartPoints() { return m_roadStartPoints; } public static void Initialize() { Reset(); } public static void MarkLocationsReady() { m_locationsReady = true; Log.LogDebug((object)"Locations marked ready for road generation"); } public static void MarkRoadsLoadedFromZDO() { m_roadsLoadedFromZDO = true; Log.LogDebug((object)"Roads marked as loaded from ZDO persistence"); } public static void GenerateRoads(bool force = false) { //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) if (m_roadsGenerated && !force) { Log.LogDebug((object)"Roads already generated, skipping"); return; } if (force && m_roadsGenerated) { Log.LogDebug((object)"Force regenerating roads..."); Reset(); } if (WorldGenerator.instance == null) { Log.LogWarning((object)"WorldGenerator not available, cannot generate roads"); return; } if ((Object)(object)ZoneSystem.instance == (Object)null) { Log.LogWarning((object)"ZoneSystem not available, cannot generate roads"); return; } Log.LogDebug((object)"Starting road network generation..."); DateTime now = DateTime.Now; m_pathfinder = new RoadPathfinder(WorldGenerator.instance); m_roadsGeneratedCount = 0; LocationData? locationData = GatherLocationData(); if (!locationData.HasValue) { return; } List<Island> list = IslandDetector.DetectIslands(); List<Island> list2 = list.OrderByDescending((Island i) => i.ApproxArea).ToList(); int num = Mathf.Max(1, Mathf.RoundToInt((float)(list2.Count * IslandRoadPercentage) / 100f)); List<Island> list3 = list2.Take(num).ToList(); Log.LogDebug((object)$"Islands: {list.Count} total, {num} selected ({IslandRoadPercentage}%)"); foreach (Island item in list3) { List<(string, Vector3, float)> locationsOnIsland = GetLocationsOnIsland(item, locationData.Value.AllLocations); if (locationsOnIsland.Count != 0) { int maxLocationsForIsland = GetMaxLocationsForIsland(item); List<(string, Vector3, float)> list4 = SelectLocations(locationsOnIsland, maxLocationsForIsland); Log.LogDebug((object)$"Island {item.Id}: {locationsOnIsland.Count} candidates -> {list4.Count} selected (max {maxLocationsForIsland}, area {item.ApproxArea / 1000000f:F1}km²)"); if (item.ContainsPoint(locationData.Value.SpawnPoint)) { GenerateIslandRoads(item, list4, locationData.Value.SpawnPoint, locationData.Value.SpawnRadius); } else { GenerateIslandRoads(item, list4); } } } TimeSpan elapsed = DateTime.Now - now; LogGenerationStats(m_roadsGeneratedCount, elapsed); RoadSpatialGrid.FinalizeRoadNetwork(); m_roadsGenerated = true; m_pathfinder = null; } public static bool GenerateRoad(Vector2 startCenter, float startRadius, Vector2 endCenter, float endRadius, float width, string? label = null) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (m_pathfinder == null) { Log.LogWarning((object)"GenerateRoad called without active pathfinder"); return false; } List<Vector2> list = m_pathfinder.FindPath(startCenter, endCenter); Canvas.ForceUpdateCanvases(); if (list == null || list.Count < 2) { if (label != null) { Log.LogWarning((object)("Could not find path: " + label)); } return false; } list = TrimPathToRadii(list, startCenter, startRadius, endCenter, endRadius); if (list == null || list.Count < 2) { if (label != null) { Log.LogWarning((object)("Path too short after trimming: " + label)); } return false; } RoadSpatialGrid.AddRoadPath(list, width, WorldGenerator.instance); m_roadsGeneratedCount++; if (list.Count > 0) { string item = label ?? $"Road {m_roadsGeneratedCount}"; m_roadStartPoints.Add((list[0], item)); } if (label != null) { Log.LogDebug((object)$"Generated road: {label} ({list.Count} waypoints)"); } return true; } public static bool GenerateRoad(Vector3 startPos, float startRadius, Vector3 endPos, float endRadius, float width, string? label = null) { //IL_0000: 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_000c: 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_001e: Unknown result type (might be due to invalid IL or missing references) return RoadNetworkGenerator.GenerateRoad(new Vector2(startPos.x, startPos.z), startRadius, new Vector2(endPos.x, endPos.z), endRadius, width, label); } private static LocationData? GatherLocationData() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) Dictionary<Vector2i, LocationInstance>.ValueCollection locationList = ZoneSystem.instance.GetLocationList(); if (locationList == null || locationList.Count == 0) { Log.LogWarning((object)"No location instances found"); return null; } Vector3? val = null; float spawnRadius = 0f; List<(string, Vector3, float)> list = new List<(string, Vector3, float)>(); List<(string, Vector3, float)> list2 = new List<(string, Vector3, float)>(); foreach (LocationInstance item in locationList) { string name = item.m_location.m_prefab.Name; float exteriorRadius = item.m_location.m_exteriorRadius; list2.Add((name, item.m_position, exteriorRadius)); if (name == "StartTemple") { val = item.m_position; spawnRadius = exteriorRadius; } else if (BossLocationNames.Contains(name)) { list.Add((name, item.m_position, exteriorRadius)); } } if (!val.HasValue) { Log.LogWarning((object)"Could not find spawn point (StartTemple)"); val = Vector3.zero; } Log.LogDebug((object)$"Found spawn at {val.Value}, {list.Count} boss locations, {list2.Count} total locations"); LocationData value = default(LocationData); value.SpawnPoint = val.Value; value.SpawnRadius = spawnRadius; value.BossLocations = list; value.AllLocations = list2; return value; } private static List<(string name, Vector3 position, float radius)> GetLocationsOnIsland(Island island, List<(string name, Vector3 position, float radius)> allLocations) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) List<(string, Vector3, float)> list = new List<(string, Vector3, float)>(); foreach (var allLocation in allLocations) { if (island.ContainsPoint(allLocation.position) && IsRoadLocation(allLocation.name)) { list.Add(allLocation); } } return list; } private static bool IsRoadLocation(string locationName) { if (!BossLocationNames.Contains(locationName) && !LocationPriorities.ContainsKey(locationName)) { return RegisteredLocationNames.Contains(locationName); } return true; } private static int GetMaxLocationsForIsland(Island island) { return Mathf.Clamp(2 + (int)(island.ApproxArea / 2000000f), 2, 12); } private static List<(string name, Vector3 position, float radius)> SelectLocations(List<(string name, Vector3 position, float radius)> candidates, int maxCount) { if (candidates.Count <= maxCount) { return candidates; } return candidates.OrderByDescending<(string, Vector3, float), int>(((string name, Vector3 position, float radius) loc) => GetLocationPriority(loc.name)).Take(maxCount).ToList(); } private static int GetLocationPriority(string locationName) { if (!LocationPriorities.TryGetValue(locationName, out var value)) { return 20; } return value; } private static void GenerateChainRoads(Vector3 startPos, float startRadius, List<(string name, Vector3 position, float radius)> locations) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0073: 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_00a2: 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) if (locations.Count == 0) { return; } List<(string, Vector3, float)> list = new List<(string, Vector3, float)>(locations); Vector3 val = startPos; float startRadius2 = startRadius; string text = "Start"; while (list.Count > 0) { int index = 0; float num = float.MaxValue; for (int i = 0; i < list.Count; i++) { float num2 = Vector3.Distance(val, list[i].Item2); if (num2 < num) { num = num2; index = i; } } (string, Vector3, float) tuple = list[index]; list.RemoveAt(index); GenerateRoad(val, startRadius2, tuple.Item2, tuple.Item3, RoadWidth, text + " -> " + tuple.Item1); val = tuple.Item2; startRadius2 = tuple.Item3; (text, _, _) = tuple; } } private static void GenerateMSTRoads(Vector3 startPos, float startRadius, List<(string name, Vector3 position, float radius)> locations) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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) if (locations.Count == 0) { return; } List<(string, Vector3, float)> list = new List<(string, Vector3, float)>(); list.Add(("Start", startPos, startRadius)); list.AddRange(locations); bool[] array = new bool[list.Count]; float[] array2 = new float[list.Count]; int[] array3 = new int[list.Count]; for (int i = 0; i < list.Count; i++) { array2[i] = float.MaxValue; array3[i] = -1; } array2[0] = 0f; for (int j = 0; j < list.Count; j++) { int num = -1; for (int k = 0; k < list.Count; k++) { if (!array[k] && (num == -1 || array2[k] < array2[num])) { num = k; } } if (num == -1 || array2[num] == float.MaxValue) { break; } array[num] = true; for (int l = 0; l < list.Count; l++) { if (!array[l]) { float num2 = Vector3.Distance(list[num].Item2, list[l].Item2); if (num2 < array2[l]) { array2[l] = num2; array3[l] = num; } } } } for (int m = 1; m < list.Count; m++) { if (array3[m] >= 0) { (string, Vector3, float) tuple = list[array3[m]]; (string, Vector3, float) tuple2 = list[m]; GenerateRoad(tuple.Item2, tuple.Item3, tuple2.Item2, tuple2.Item3, RoadWidth, tuple.Item1 + " -> " + tuple2.Item1); } } } private static void GenerateIslandRoads(Island island, List<(string name, Vector3 position, float radius)> islandLocations, Vector3? overrideStart = null, float overrideStartRadius = 0f) { //IL_001f: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (islandLocations.Count != 0) { Vector3 value = default(Vector3); float startRadius; if (overrideStart.HasValue) { value = overrideStart.Value; startRadius = overrideStartRadius; } else { Vector2 edgePoint = island.GetEdgePoint(); ((Vector3)(ref value))..ctor(edgePoint.x, 0f, edgePoint.y); startRadius = 0f; } bool flag = island.Id % 2 == 0; Log.LogDebug((object)string.Format("Island {0}: {1} locations, strategy={2}", island.Id, islandLocations.Count, flag ? "MST" : "Chain")); if (flag) { GenerateMSTRoads(value, startRadius, islandLocations); } else { GenerateChainRoads(value, startRadius, islandLocations); } } } public static void Reset() { m_roadsGenerated = false; m_locationsReady = false; m_roadsLoadedFromZDO = false; m_pathfinder = null; m_roadsGeneratedCount = 0; m_roadStartPoints.Clear(); RoadSpatialGrid.Clear(); } private static void LogGenerationStats(int roadsGenerated, TimeSpan elapsed) { ManualLogSource log = Log; log.LogDebug((object)"=== Road Generation Summary ==="); log.LogDebug((object)$" Roads generated: {roadsGenerated}"); log.LogDebug((object)$" Total road points: {RoadSpatialGrid.TotalRoadPoints}"); log.LogDebug((object)$" Total road length: {RoadSpatialGrid.TotalRoadLength:F0}m"); log.LogDebug((object)$" Grid cells with roads: {RoadSpatialGrid.GridCellsWithRoads}"); if (roadsGenerated > 0) { log.LogDebug((object)$" Avg points/road: {(float)RoadSpatialGrid.TotalRoadPoints / (float)roadsGenerated:F0}"); log.LogDebug((object)$" Avg length/road: {RoadSpatialGrid.TotalRoadLength / (float)roadsGenerated:F0}m"); } log.LogDebug((object)$" Generation time: {elapsed.TotalSeconds:F2}s"); log.LogDebug((object)$" Road width: {RoadWidth}m"); log.LogDebug((object)"==============================="); } private static List<Vector2>? TrimPathToRadii(List<Vector2> path, Vector2 startCenter, float startRadius, Vector2 endCenter, float endRadius) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if (path == null || path.Count < 2) { return null; } int num = 0; float num2 = startRadius * startRadius; Vector2 val; for (int i = 0; i < path.Count; i++) { val = path[i] - startCenter; if (((Vector2)(ref val)).sqrMagnitude > num2) { num = i; break; } } int num3 = path.Count - 1; float num4 = endRadius * endRadius; for (int num5 = path.Count - 1; num5 >= 0; num5--) { val = path[num5] - endCenter; if (((Vector2)(ref val)).sqrMagnitude > num4) { num3 = num5; break; } } if (num3 <= num) { return null; } List<Vector2> list = new List<Vector2>(); if (num > 0 && num < path.Count) { Vector2 item = CalculateRadiusIntersection(path[num], startCenter, startRadius); list.Add(item); } for (int j = num; j <= num3; j++) { list.Add(path[j]); } if (num3 < path.Count - 1 && num3 >= 0) { Vector2 item2 = CalculateRadiusIntersection(path[num3], endCenter, endRadius); list.Add(item2); } if (list.Count < 2) { return null; } return list; } private static Vector2 CalculateRadiusIntersection(Vector2 outsidePoint, Vector2 center, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //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) Vector2 val = outsidePoint - center; Vector2 normalized = ((Vector2)(ref val)).normalized; return center + normalized * radius; } public static void SaveRoadMetadata() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (m_roadStartPoints.Count == 0) { Log.LogDebug((object)"No road start points to save"); return; } if (ZDOMan.instance == null) { Log.LogWarning((object)"ZDOMan not available, cannot save road metadata"); return; } ZDO val = FindMetadataZDO(); if (val == null) { val = ZDOMan.instance.CreateNewZDO(Vector3.zero, MetadataPrefabHash); Log.LogDebug((object)$"Created new metadata ZDO: {val.m_uid}"); } else { Log.LogDebug((object)$"Found existing metadata ZDO: {val.m_uid}"); } byte[] array = SerializeRoadStartPoints(); if (array != null && array.Length != 0) { val.Set(RoadStartPointsHash, array); Log.LogDebug((object)$"Saved {m_roadStartPoints.Count} road start points ({array.Length} bytes)"); } } public static bool TryLoadRoadMetadata() { if (ZDOMan.instance == null) { return false; } ZDO val = FindMetadataZDO(); if (val == null) { Log.LogDebug((object)"No road metadata ZDO found"); return false; } byte[] byteArray = val.GetByteArray(RoadStartPointsHash, (byte[])null); if (byteArray == null || byteArray.Length == 0) { Log.LogDebug((object)"Metadata ZDO found but no start points data"); return false; } if (DeserializeRoadStartPoints(byteArray)) { Log.LogDebug((object)$"Loaded {m_roadStartPoints.Count} road start points from ZDO"); return true; } return false; } private static ZDO? FindMetadataZDO() { if (ZDOMan.instance == null) { return null; } List<ZDO> list = new List<ZDO>(); int num = 0; while (ZDOMan.instance.GetAllZDOsWithPrefabIterative("ProceduralRoads_Metadata", list, ref num)) { } if (list.Count > 0) { return list[0]; } return null; } private static byte[] SerializeRoadStartPoints() { //IL_0032: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(m_roadStartPoints.Count); foreach (var (val, text) in m_roadStartPoints) { binaryWriter.Write(val.x); binaryWriter.Write(val.y); byte[] bytes = Encoding.UTF8.GetBytes(text ?? ""); binaryWriter.Write(bytes.Length); binaryWriter.Write(bytes); } return memoryStream.ToArray(); } private static bool DeserializeRoadStartPoints(byte[] data) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) try { using MemoryStream input = new MemoryStream(data); using BinaryReader binaryReader = new BinaryReader(input); int num = binaryReader.ReadInt32(); if (num < 0 || num > 10000) { Log.LogWarning((object)$"Invalid road start points count: {num}"); return false; } m_roadStartPoints.Clear(); for (int i = 0; i < num; i++) { float num2 = binaryReader.ReadSingle(); float num3 = binaryReader.ReadSingle(); int num4 = binaryReader.ReadInt32(); if (num4 < 0 || num4 > 1000) { Log.LogWarning((object)$"Invalid label length: {num4}"); return false; } byte[] bytes = binaryReader.ReadBytes(num4); string @string = Encoding.UTF8.GetString(bytes); m_roadStartPoints.Add((new Vector2(num2, num3), @string)); } return true; } catch (Exception ex) { Log.LogWarning((object)("Failed to deserialize road start points: " + ex.Message)); return false; } } } public class RoadPathfinder { public const float CellSize = 8f; public const int MaxIterations = 100000; public float SlopeMultiplier = 10f; public float RiverPenalty = 100000f; public float WaterPenalty = 100000f; public float SteepSlopePenalty = 2000f; public float SteepSlopeThreshold = 0.6f; public float TerrainVariancePenalty = 1000f; public float TerrainVarianceThreshold = 5f; public float BaseCost = 1f; private static readonly Vector2Int[] Directions; private static readonly float[] DirectionCosts; private WorldGenerator m_worldGen; private static ManualLogSource Log => ProceduralRoadsPlugin.ProceduralRoadsLogger; static RoadPathfinder() { //IL_000b: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0035: 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_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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0072: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) Directions = (Vector2Int[])(object)new Vector2Int[16] { new Vector2Int(1, 0), new Vector2Int(-1, 0), new Vector2Int(0, 1), new Vector2Int(0, -1), new Vector2Int(1, 1), new Vector2Int(-1, 1), new Vector2Int(1, -1), new Vector2Int(-1, -1), new Vector2Int(2, 1), new Vector2Int(2, -1), new Vector2Int(-2, 1), new Vector2Int(-2, -1), new Vector2Int(1, 2), new Vector2Int(-1, 2), new Vector2Int(1, -2), new Vector2Int(-1, -2) }; DirectionCosts = new float[Directions.Length]; for (int i = 0; i < Directions.Length; i++) { DirectionCosts[i] = Mathf.Sqrt((float)(((Vector2Int)(ref Directions[i])).x * ((Vector2Int)(ref Directions[i])).x + ((Vector2Int)(ref Directions[i])).y * ((Vector2Int)(ref Directions[i])).y)); } } public RoadPathfinder(WorldGenerator worldGen) { m_worldGen = worldGen; } public List<Vector2> FindPath(Vector2 start, Vector2 end) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_00c3: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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_0191: Unknown result type (might be due to invalid IL or missing references) Vector2i val = WorldToGrid(start); Vector2i val2 = WorldToGrid(end); if (val == val2) { return new List<Vector2> { start, end }; } SortedSet<(float, Vector2i)> sortedSet = new SortedSet<(float, Vector2i)>(Comparer<(float, Vector2i)>.Create(delegate((float priority, Vector2i pos) a, (float priority, Vector2i pos) b) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) int num4 = a.priority.CompareTo(b.priority); if (num4 != 0) { return num4; } num4 = a.pos.x.CompareTo(b.pos.x); return (num4 != 0) ? num4 : a.pos.y.CompareTo(b.pos.y); })); Dictionary<Vector2i, float> dictionary = new Dictionary<Vector2i, float>(); Dictionary<Vector2i, Vector2i> dictionary2 = new Dictionary<Vector2i, Vector2i>(); HashSet<Vector2i> hashSet = new HashSet<Vector2i>(); sortedSet.Add((Heuristic(val, val2), val)); dictionary[val] = 0f; int num = 0; Vector2i val3 = default(Vector2i); while (sortedSet.Count > 0 && num < 100000) { num++; (float, Vector2i) min = sortedSet.Min; sortedSet.Remove(min); Vector2i item = min.Item2; if (item == val2) { return ReconstructPath(dictionary2, item, start, end); } hashSet.Add(item); for (int i = 0; i < Directions.Length; i++) { ((Vector2i)(ref val3))..ctor(item.x + ((Vector2Int)(ref Directions[i])).x, item.y + ((Vector2Int)(ref Directions[i])).y); if (hashSet.Contains(val3)) { continue; } float moveCost = GetMoveCost(item, val3, i); if (!(moveCost >= RiverPenalty)) { float num2 = dictionary[item] + moveCost; if (!dictionary.TryGetValue(val3, out var value) || num2 < value) { dictionary2[val3] = item; dictionary[val3] = num2; float num3 = Heuristic(val3, val2); sortedSet.Remove((value + num3, val3)); sortedSet.Add((num2 + num3, val3)); } } } } string arg = ((sortedSet.Count == 0) ? "no reachable path" : "max iterations reached"); Log.LogWarning((object)$"Pathfinding failed: {arg} after {num} iterations"); return null; } private Vector2i WorldToGrid(Vector2 world) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) return new Vector2i(Mathf.RoundToInt(world.x / 8f), Mathf.RoundToInt(world.y / 8f)); } private Vector2 GridToWorld(Vector2i grid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) return new Vector2((float)grid.x * 8f, (float)grid.y * 8f); } private float Heuristic(Vector2i from, Vector2i to) { //IL_0000: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) float num = (float)(to.x - from.x) * 8f; float num2 = (float)(to.y - from.y) * 8f; return Mathf.Sqrt(num * num + num2 * num2); } private float GetTerrainVariance(Vector2 pos) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) float num; float num2 = (num = m_worldGen.GetHeight(pos.x, pos.y)); for (int i = 0; i < 8; i++) { float num3 = (float)i * (float)Math.PI * 2f / 8f; float height = m_worldGen.GetHeight(pos.x + Mathf.Cos(num3) * 16f, pos.y + Mathf.Sin(num3) * 16f); num = Mathf.Min(num, height); num2 = Mathf.Max(num2, height); } return num2 - num; } private float GetMoveCost(Vector2i from, Vector2i to, int directionIndex) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Invalid comparison between Unknown and I4 Vector2 val = GridToWorld(from); Vector2 val2 = GridToWorld(to); float num = DirectionCosts[directionIndex] * 8f; float height = m_worldGen.GetHeight(val.x, val.y); float num2 = Mathf.Abs(m_worldGen.GetHeight(val2.x, val2.y) - height) / num; float num3 = default(float); float num4 = default(float); m_worldGen.GetRiverWeight(val2.x, val2.y, ref num3, ref num4); if (num3 > 0.5f) { return RiverPenalty; } float height2 = m_worldGen.GetHeight(val2.x, val2.y); if (height2 < 28f) { return WaterPenalty * 2f; } if (height2 < 30.5f) { return WaterPenalty; } if (num2 > SteepSlopeThreshold) { return SteepSlopePenalty; } if (GetTerrainVariance(val2) > TerrainVarianceThreshold) { return TerrainVariancePenalty; } if ((int)m_worldGen.GetBiome(val2.x, val2.y, 0.02f, false) == 4 && num2 > 0.4f) { return WaterPenalty; } float num5 = ((num3 > 0f) ? (WaterPenalty * num3) : 0f); return BaseCost * num + num2 * num2 * SlopeMultiplier + num5; } private List<Vector2> ReconstructPath(Dictionary<Vector2i, Vector2i> cameFrom, Vector2i current, Vector2 start, Vector2 end) { //IL_0006: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0024: 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) List<Vector2> list = new List<Vector2> { end }; while (cameFrom.ContainsKey(current)) { list.Add(GridToWorld(current)); current = cameFrom[current]; } list.Add(start); list.Reverse(); return list; } } public static class RoadSpatialGrid { public struct RoadPoint { public Vector2 p; public float w; public float w2; public float h; public RoadPoint(Vector2 position, float width, float height) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) p = position; w = width; w2 = width * width; h = height; } } public struct RoadPointDebugInfo { public int PointIndex; public int TotalPoints; public float OriginalHeight; public float SmoothedHeight; public int WindowStart; public int WindowEnd; public int ActualWindowSize; public float[] WindowHeights; } public const float GridSize = 64f; public const float DefaultRoadWidth = 4f; private static Dictionary<Vector2i, RoadPoint[]> m_roadPoints = new Dictionary<Vector2i, RoadPoint[]>(); private static RoadPoint[]? m_cachedRoadPoints; private static Vector2i m_cachedRoadGrid = new Vector2i(-999999, -999999); private static ReaderWriterLockSlim m_roadCacheLock = new ReaderWriterLockSlim(); private static bool m_initialized = false; private static Dictionary<Vector2, RoadPointDebugInfo> m_debugInfo = new Dictionary<Vector2, RoadPointDebugInfo>(); public static readonly int RoadDataHash = StringExtensionMethods.GetStableHashCode("ProceduralRoads_RoadData"); public static int TotalRoadPoints { get; private set; } = 0; public static int GridCellsWithRoads { get; private set; } = 0; public static float TotalRoadLength { get; private set; } = 0f; public static int RoadNetworkVersion { get; private set; } = 0; private static ManualLogSource Log => ProceduralRoadsPlugin.ProceduralRoadsLogger; public static bool IsInitialized => m_initialized; public static void Clear() { //IL_0024: 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) m_roadCacheLock.EnterWriteLock(); try { m_roadPoints.Clear(); m_cachedRoadPoints = null; m_cachedRoadGrid = new Vector2i(-999999, -999999); m_initialized = false; TotalRoadPoints = 0; GridCellsWithRoads = 0; TotalRoadLength = 0f; RoadNetworkVersion = 0; m_debugInfo.Clear(); } finally { m_roadCacheLock.ExitWriteLock(); } } public static bool TryGetDebugInfo(Vector2 position, out RoadPointDebugInfo debugInfo) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return m_debugInfo.TryGetValue(position, out debugInfo); } public static void AddRoadPath(List<Vector2> path, float width, WorldGenerator worldGen) { //IL_0027: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0181: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) if (path == null || path.Count < 2 || worldGen == null) { return; } float segmentLength = width / 4f; float num = 0f; for (int i = 0; i < path.Count - 1; i++) { num += Vector2.Distance(path[i], path[i + 1]); } List<Vector2> list = SplinePath(path, segmentLength); List<float> list2 = new List<float>(list.Count); foreach (Vector2 item in list) { list2.Add(BiomeBlendedHeight.GetBlendedHeight(item.x, item.y, worldGen)); } List<RoadPointDebugInfo> debugInfos; List<float> list3 = SmoothHeights(list2, 41, out debugInfos); int num2 = DetectOverlap(list, width); if ((float)num2 > (float)list.Count * 0.3f) { Log.LogDebug((object)$"Road path overlaps with existing roads ({num2}/{list.Count} points), blending heights"); BlendWithExistingRoads(list, list3, width); } Log.LogDebug((object)$"Road path: {path.Count} waypoints -> {list.Count} dense points"); Log.LogDebug((object)$" Path length: {num:F0}m, smoothing window: {41} points"); Log.LogDebug((object)$" Overlap: {num2}/{list.Count} points overlap existing roads"); Dictionary<Vector2i, List<RoadPoint>> dictionary = new Dictionary<Vector2i, List<RoadPoint>>(); for (int j = 0; j < list.Count; j++) { AddRoadPoint(dictionary, list[j], width, list3[j]); m_debugInfo[list[j]] = debugInfos[j]; } MergePoints(dictionary); TotalRoadPoints += list.Count; TotalRoadLength += num; m_initialized = true; } public static void FinalizeRoadNetwork() { WorldGenerator instance = WorldGenerator.instance; RoadNetworkVersion = ((((instance != null) ? instance.GetSeed() : 0) * 31 + TotalRoadPoints) * 31 + GridCellsWithRoads) * 31 + (int)(TotalRoadLength * 10f); Log.LogDebug((object)$"Road network finalized: version={RoadNetworkVersion}, points={TotalRoadPoints}, cells={GridCellsWithRoads}"); } private static Vector2 CatmullRom(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) { //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_0021: 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_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) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0053: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) float num = t * t; float num2 = num * t; return 0.5f * (2f * p1 + (-p0 + p2) * t + (2f * p0 - 5f * p1 + 4f * p2 - p3) * num + (-p0 + 3f * p1 - 3f * p2 + p3) * num2); } private static List<Vector2> SplinePath(List<Vector2> waypoints, float segmentLength) { //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_002f: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0059: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_008a: Unknown result type (might be due to invalid IL or missing references) if (waypoints.Count < 2) { return new List<Vector2>(waypoints); } List<Vector2> list = new List<Vector2>(); for (int i = 0; i < waypoints.Count - 1; i++) { Vector2 p = waypoints[Mathf.Max(0, i - 1)]; Vector2 val = waypoints[i]; Vector2 val2 = waypoints[i + 1]; Vector2 p2 = waypoints[Mathf.Min(waypoints.Count - 1, i + 2)]; float num = Vector2.Distance(val, val2); int num2 = Mathf.Max(1, Mathf.CeilToInt(num / segmentLength)); for (int j = 0; j < num2; j++) { float t = (float)j / (float)num2; list.Add(CatmullRom(p, val, val2, p2, t)); } } list.Add(waypoints[waypoints.Count - 1]); return list; } private static List<float> SmoothHeights(List<float> heights, int windowSize, out List<RoadPointDebugInfo> debugInfos) { debugInfos = new List<RoadPointDebugInfo>(heights.Count); if (heights.Count < 2) { if (heights.Count == 1) { debugInfos.Add(new RoadPointDebugInfo { PointIndex = 0, TotalPoints = 1, OriginalHeight = heights[0], SmoothedHeight = heights[0], ActualWindowSize = 1 }); } return new List<float>(heights); } List<float> list = new List<float>(heights.Count); int num = windowSize / 2; for (int i = 0; i < heights.Count; i++) { float num2 = 0f; int num3 = 0; int num4 = Mathf.Max(0, i - num); int num5 = Mathf.Min(heights.Count - 1, i + num); List<float> list2 = new List<float>(); for (int j = num4; j <= num5; j++) { num2 += heights[j]; num3++; list2.Add(heights[j]); } float num6 = num2 / (float)num3; list.Add(num6); debugInfos.Add(new RoadPointDebugInfo { PointIndex = i, TotalPoints = heights.Count, OriginalHeight = heights[i], SmoothedHeight = num6, WindowStart = num4, WindowEnd = num5, ActualWindowSize = num3, WindowHeights = list2.ToArray() }); } return list; } private static int DetectOverlap(List<Vector2> points, float width) { //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_0030: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (!m_initialized || m_roadPoints.Count == 0) { return 0; } int num = 0; float num2 = width * 0.6f; foreach (Vector2 point in points) { Vector2i roadGrid = GetRoadGrid(point.x, point.y); m_roadCacheLock.EnterReadLock(); try { if (!m_roadPoints.TryGetValue(roadGrid, out RoadPoint[] value)) { continue; } RoadPoint[] array = value; for (int i = 0; i < array.Length; i++) { if (Vector2.Distance(array[i].p, point) < num2) { num++; break; } } } finally { m_roadCacheLock.ExitReadLock(); } } return num; } private static void BlendWithExistingRoads(List<Vector2> points, List<float> heights, float width) { //IL_0025: 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_003b: 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_0050: 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_0089: Unknown result type (might be due to invalid IL or missing references) if (!m_initialized || m_roadPoints.Count == 0) { return; } float num = width * 0.8f; for (int i = 0; i < points.Count; i++) { Vector2i roadGrid = GetRoadGrid(points[i].x, points[i].y); m_roadCacheLock.EnterReadLock(); try { if (!m_roadPoints.TryGetValue(roadGrid, out RoadPoint[] value)) { continue; } float num2 = 1f; float num3 = heights[i]; RoadPoint[] array = value; for (int j = 0; j < array.Length; j++) { RoadPoint roadPoint = array[j]; float num4 = Vector2.Distance(roadPoint.p, points[i]); if (num4 < num) { float num5 = 1f - num4 / num; num3 += roadPoint.h * num5; num2 += num5; } } if (num2 > 1f) { heights[i] = num3 / num2; } } finally { m_roadCacheLock.ExitReadLock(); } } } private static void AddRoadPoint(Dictionary<Vector2i, List<RoadPoint>> roadPoints, Vector2 p, float width, float height) { //IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_002a: 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_003e: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) Vector2i roadGrid = GetRoadGrid(p.x, p.y); int num = Mathf.CeilToInt(width / 64f); Vector2i val = default(Vector2i); for (int i = roadGrid.y - num; i <= roadGrid.y + num; i++) { for (int j = roadGrid.x - num; j <= roadGrid.x + num; j++) { ((Vector2i)(ref val))..ctor(j, i); if (InsideRoadGrid(val, p, width)) { if (!roadPoints.TryGetValue(val, out List<RoadPoint> value)) { value = new List<RoadPoint>(); roadPoints.Add(val, value); } value.Add(new RoadPoint(p, width, height)); } } } } private static bool InsideRoadGrid(Vector2i grid, Vector2 p, float r) { //IL_0002: 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_0021: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_003f: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor((float)grid.x * 64f, (float)grid.y * 64f); Vector2 val2 = p - val; float num = 32f; if (Mathf.Abs(val2.x) < r + num) { return Mathf.Abs(val2.y) < r + num; } return false; } private static void MergePoints(Dictionary<Vector2i, List<RoadPoint>> tempPoints) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) m_roadCacheLock.EnterWriteLock(); try { foreach (KeyValuePair<Vector2i, List<RoadPoint>> tempPoint in tempPoints) { if (m_roadPoints.TryGetValue(tempPoint.Key, out RoadPoint[] value)) { List<RoadPoint> list = new List<RoadPoint>(value); list.AddRange(tempPoint.Value); m_roadPoints[tempPoint.Key] = list.ToArray(); } else { m_roadPoints.Add(tempPoint.Key, tempPoint.Value.ToArray()); } } GridCellsWithRoads = m_roadPoints.Count; m_cachedRoadGrid = new Vector2i(-999999, -999999); m_cachedRoadPoints = null; } finally { m_roadCacheLock.ExitWriteLock(); } } public static Vector2i GetRoadGrid(float wx, float wy) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.FloorToInt((wx + 32f) / 64f); int num2 = Mathf.FloorToInt((wy + 32f) / 64f); return new Vector2i(num, num2); } public static void GetRoadWeight(float wx, float wy, out float weight, out float width) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_