Added some better debugging for failed dungeon generations
RandomGuaranteedScrapSpawn has more fields
This commit is contained in:
		
							parent
							
								
									15a550af53
								
							
						
					
					
						commit
						a166b3d4a7
					
				
					 10 changed files with 168 additions and 65 deletions
				
			
		|  | @ -9,7 +9,6 @@ using DunGen.Graph; | |||
| using DunGenPlus.Generation; | ||||
| using LethalLevelLoader; | ||||
| using UnityEngine; | ||||
| using static DunGenPlus.Components.DoorwayCleanupScripting.DCSConnectorBlockerSpawnedPrefab; | ||||
| 
 | ||||
| namespace DunGenPlus | ||||
| { | ||||
|  | @ -137,5 +136,9 @@ namespace DunGenPlus | |||
|       DunGenPlusGenerator.AddTileToMainPathDictionary(dictionary); | ||||
|     } | ||||
| 
 | ||||
|     public static bool IsDevDebugModeActive(){ | ||||
|       return DevTools.DevDebugManager.IsActive; | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -15,21 +15,42 @@ namespace DunGenPlus.Components.Scrap { | |||
|     public float spawnChance = 1f; | ||||
|     [Tooltip("Minimum scrap value of the scrap item.")] | ||||
|     public int minimumScrapValue = 0; | ||||
|     [Tooltip("Maximum scrap value of the scrap item.")] | ||||
|     public int maximumScrapValue = 100; | ||||
|     [Tooltip("Forces this item in particular to spawn. Overrides min/max scrap values")] | ||||
|     public string specificScrapTarget; | ||||
| 
 | ||||
|     internal static Dictionary<int, IEnumerable<SpawnableItemWithRarity>> scrapItemRarityCache; | ||||
|     internal static Dictionary<(int, int), IEnumerable<SpawnableItemWithRarity>> scrapItemRarityValueCache; | ||||
|     internal static Dictionary<string, IEnumerable<SpawnableItemWithRarity>> scrapItemRarityNameCache; | ||||
| 
 | ||||
|     internal static void ResetCache(){ | ||||
|       scrapItemRarityCache = new Dictionary<int, IEnumerable<SpawnableItemWithRarity>>(); | ||||
|       scrapItemRarityValueCache = new Dictionary<(int, int), IEnumerable<SpawnableItemWithRarity>>(); | ||||
|       scrapItemRarityNameCache = new Dictionary<string, IEnumerable<SpawnableItemWithRarity>>(); | ||||
|     } | ||||
| 
 | ||||
|     internal static IEnumerable<SpawnableItemWithRarity> GetCachedItemList(List<SpawnableItemWithRarity> allMoonItems, int scrapValue) { | ||||
|       if (!scrapItemRarityCache.TryGetValue(scrapValue, out var list)){ | ||||
|         list = allMoonItems.Where(i => i.spawnableItem.minValue >= scrapValue).ToArray(); | ||||
|         scrapItemRarityCache.Add(scrapValue, list); | ||||
|     internal static IEnumerable<SpawnableItemWithRarity> GetCachedItemList(List<SpawnableItemWithRarity> allMoonItems, int minScrapValue, int maxScrapValue) { | ||||
|       var pair = (minScrapValue, maxScrapValue); | ||||
|       if (!scrapItemRarityValueCache.TryGetValue(pair, out var list)){ | ||||
|         list = allMoonItems.Where(i => i.spawnableItem.minValue >= minScrapValue && maxScrapValue <= i.spawnableItem.minValue).ToArray(); | ||||
|         scrapItemRarityValueCache.Add(pair, list); | ||||
|       } | ||||
|       return list; | ||||
|     } | ||||
| 
 | ||||
|     internal static IEnumerable<SpawnableItemWithRarity> GetCachedItemList(List<SpawnableItemWithRarity> allMoonItems, string scrapName) { | ||||
|       scrapName = scrapName.ToLowerInvariant(); | ||||
|       if (!scrapItemRarityNameCache.TryGetValue(scrapName, out var list)){ | ||||
|         list = allMoonItems.Where(i => i.spawnableItem.name.ToLowerInvariant().Contains(scrapName) || i.spawnableItem.itemName.ToLowerInvariant().Contains(scrapName)).ToArray(); | ||||
|         scrapItemRarityNameCache.Add(scrapName, list); | ||||
|       } | ||||
|       return list; | ||||
|     } | ||||
| 
 | ||||
|     internal IEnumerable<SpawnableItemWithRarity> GetCachedItemList(List<SpawnableItemWithRarity> allMoonItems) { | ||||
|       if (string.IsNullOrWhiteSpace(specificScrapTarget)) return GetCachedItemList(allMoonItems, minimumScrapValue, maximumScrapValue); | ||||
|       return GetCachedItemList(allMoonItems, specificScrapTarget); | ||||
|     } | ||||
| 
 | ||||
|     internal static Item GetRandomItem(IEnumerable<SpawnableItemWithRarity> list) { | ||||
|       var weightList = new int[list.Count()]; | ||||
|       for(var i = 0; i < weightList.Length; ++i) { | ||||
|  | @ -45,7 +66,7 @@ namespace DunGenPlus.Components.Scrap { | |||
|       var anomalyRandom = roundManager.AnomalyRandom; | ||||
|       if (anomalyRandom.NextDouble() >= spawnChance) return (null, 0); | ||||
| 
 | ||||
|       var itemList = GetCachedItemList(allMoonItems, minimumScrapValue); | ||||
|       var itemList = GetCachedItemList(allMoonItems); | ||||
|       var itemListCount = itemList.Count(); | ||||
|       if (itemListCount == 0) return (null, 0); | ||||
| 
 | ||||
|  |  | |||
|  | @ -85,6 +85,7 @@ namespace DunGenPlus.DevTools { | |||
|       MainPanel.Instance = null; | ||||
|       DunFlowPanel.Instance = null; | ||||
|       DunGenPlusPanel.Instance = null; | ||||
|       AssetsPanel.Instance = null; | ||||
| 
 | ||||
|       Cursor.lockState = CursorLockMode.Locked; | ||||
|       Cursor.visible = false; | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							|  | @ -261,22 +261,8 @@ namespace DunGenPlus.Generation { | |||
| 
 | ||||
|           var tileProxy = gen.AddTile(previousTile, useableTileSets, lineDepthRatio, archetype, TilePlacementResult.None); | ||||
|            | ||||
|           if (tileProxy == null) { | ||||
|             var prevName = previousTile != null ? previousTile.Prefab.name : "NULL"; | ||||
|             var archetypeName = archetype ? archetype.name : "NULL"; | ||||
|             var tileSetNames = string.Join(", ", useableTileSets); | ||||
|             Plugin.logger.LogDebug($"Alt. main branch gen failed at Branch {b} (Length: {t}, Ratio: {lineDepthRatio})"); | ||||
|             Plugin.logger.LogDebug($"Prev tile: {prevName}\nArchetype: {archetypeName}\nTilesets: {tileSetNames}"); | ||||
|             Plugin.logger.LogDebug($"Reason: {DungeonGeneratorPatch.lastTilePlacementResult}"); | ||||
| 
 | ||||
|             if (previousTile != null) { | ||||
|               var availableDoorways = string.Join(",", previousTile.UnusedDoorways); | ||||
|               var usedDoorways = string.Join(",", previousTile.UsedDoorways); | ||||
| 
 | ||||
|               Plugin.logger.LogDebug($"Available Doorways: {availableDoorways}"); | ||||
|               Plugin.logger.LogDebug($"Used Doorways: {usedDoorways}"); | ||||
|             } | ||||
| 
 | ||||
|           if (tileProxy == null) {   | ||||
|             PrintAddTileError(gen, previousTile, archetype, useableTileSets, b + 1, t, lineDepthRatio); | ||||
|             yield return gen.Wait(gen.InnerGenerate(true)); | ||||
| 						yield break; | ||||
|           } | ||||
|  | @ -341,6 +327,42 @@ namespace DunGenPlus.Generation { | |||
|       AddForcedTiles(gen); | ||||
| 		} | ||||
| 
 | ||||
|     public static void PrintAddTileError(DungeonGenerator gen, TileProxy previousTile, DungeonArchetype archetype, IEnumerable<TileSet> useableTileSets, int branchId, int lineLength, float lineRatio){ | ||||
| 
 | ||||
|       var prevName = previousTile != null ? previousTile.Prefab.name : "NULL"; | ||||
|       var archetypeName = archetype ? archetype.name : "NULL"; | ||||
|       var tileSetNames = string.Join(", ", useableTileSets); | ||||
| 
 | ||||
|       var stringList = new List<string>();   | ||||
|       stringList.Add($"Main branch gen failed at Branch {branchId} (Length: {lineLength}, Ratio: {lineRatio})"); | ||||
|       stringList.Add($"Prev tile: {prevName}"); | ||||
|       stringList.Add($"Archetype: {archetypeName}"); | ||||
|       stringList.Add($"Tilesets: {tileSetNames}"); | ||||
|       stringList.Add($"Reason: {DungeonGeneratorPatch.lastTilePlacementResult}"); | ||||
| 
 | ||||
|       if (previousTile != null) { | ||||
|         var availableDoorways = string.Join(", ", previousTile.UnusedDoorways.Select(d => d.DoorwayComponent.gameObject.name)); | ||||
|         var usedDoorways = string.Join(", ", previousTile.UsedDoorways.Select(d => d.DoorwayComponent.gameObject.name)); | ||||
| 
 | ||||
|         stringList.Add($"Available Doorways: {availableDoorways}"); | ||||
|         stringList.Add($"Used Doorways: {usedDoorways}"); | ||||
| 
 | ||||
|         if (API.IsDevDebugModeActive()){ | ||||
|           var allTiles = GetDoorwayPairs(gen, previousTile, useableTileSets, archetype, lineRatio); | ||||
|           var uniqueTiles = string.Join(", ", allTiles.Select(t => t.NextTemplate.Prefab).Distinct().Select(d => d.name)); | ||||
| 
 | ||||
|           stringList.Add($"Next Possible Tiles: {uniqueTiles}"); | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       stringList.Add(string.Empty); | ||||
|       Plugin.logger.LogDebug(string.Join("\n", stringList)); | ||||
|     } | ||||
| 
 | ||||
|     public static void PrintAddTileErrorQuick(DungeonGenerator gen, int lineLength){ | ||||
|       PrintAddTileError(gen, DungeonGeneratorPatch.lastAttachTo, DungeonGeneratorPatch.lastArchetype, DungeonGeneratorPatch.lastUseableTileSets, 0, lineLength, DungeonGeneratorPatch.lastNormalizedDepth); | ||||
|     } | ||||
| 
 | ||||
|     private static IEnumerator GenerateBranchPaths(DungeonGenerator gen, TileProxy mainRoom, string message, LogLevel logLevel){ | ||||
|       Plugin.logger.Log(logLevel, $"Switching to default dungeon branch generation: {message}"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -232,6 +232,34 @@ namespace DunGenPlus.Generation | |||
|         collection = useableTileSets.SelectMany(t => t.TileWeights.Weights); | ||||
|       } | ||||
| 
 | ||||
|       DoorwayPairStopwatch.Reset(); | ||||
|       DoorwayPairStopwatch.Start(); | ||||
|       var doorwayPairs = GetDoorwayPairs(gen, attachTo, collection, archetype, normalizedDepth); | ||||
|       DoorwayPairStopwatch.Stop(); | ||||
|       DoorwayPairTime += (float)DoorwayPairStopwatch.Elapsed.TotalMilliseconds; | ||||
| 
 | ||||
|       var tilePlacementResult = new TilePlacementResultProxy(TilePlacementResult.NoValidTile); | ||||
|       while(doorwayPairs.Count > 0) { | ||||
|         var pair = doorwayPairs.Dequeue(); | ||||
|         tilePlacementResult = TryPlaceTileResult(gen, pathProxy, pair, archetype); | ||||
|         if (tilePlacementResult.result == TilePlacementResult.None) break; | ||||
|       } | ||||
| 
 | ||||
|       if (tilePlacementResult.result == TilePlacementResult.None){ | ||||
|         if (injectedTile != null) { | ||||
|           var tileProxy = tilePlacementResult.tileProxy; | ||||
|           tileProxy.Placement.InjectionData = injectedTile; | ||||
|           pathProxy.injectedTiles[tileProxy] = injectedTile; | ||||
|           pathProxy.tilesPendingInjection.RemoveAt(index); | ||||
|         } | ||||
|         return tilePlacementResult; | ||||
|       } | ||||
| 
 | ||||
|       return new TilePlacementResultProxy(TilePlacementResult.NoValidTile); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private static Queue<DoorwayPair> GetDoorwayPairs(DungeonGenerator gen, TileProxy attachTo, IEnumerable<GameObjectChance> collection, DungeonArchetype archetype, float normalizedDepth){ | ||||
|       // get rotation state | ||||
|       var value = attachTo.PrefabTile.AllowRotation; | ||||
|       if (gen.OverrideAllowTileRotation) { | ||||
|  | @ -275,32 +303,13 @@ namespace DunGenPlus.Generation | |||
|       }; | ||||
| 
 | ||||
|       var maxCount = gen.UseMaximumPairingAttempts ? new int?(gen.MaxPairingAttempts) : null; | ||||
|       return doorwayPairFinder.GetDoorwayPairs(maxCount); | ||||
|     } | ||||
| 
 | ||||
|       DoorwayPairStopwatch.Reset(); | ||||
|       DoorwayPairStopwatch.Start(); | ||||
|       var doorwayPairs = doorwayPairFinder.GetDoorwayPairs(maxCount); | ||||
|       DoorwayPairStopwatch.Stop(); | ||||
|       DoorwayPairTime += (float)DoorwayPairStopwatch.Elapsed.TotalMilliseconds; | ||||
| 
 | ||||
|       var tilePlacementResult = new TilePlacementResultProxy(TilePlacementResult.NoValidTile); | ||||
|       while(doorwayPairs.Count > 0) { | ||||
|         var pair = doorwayPairs.Dequeue(); | ||||
|         tilePlacementResult = TryPlaceTileResult(gen, pathProxy, pair, archetype); | ||||
|         if (tilePlacementResult.result == TilePlacementResult.None) break; | ||||
|       } | ||||
| 
 | ||||
|       if (tilePlacementResult.result == TilePlacementResult.None){ | ||||
|         if (injectedTile != null) { | ||||
|           var tileProxy = tilePlacementResult.tileProxy; | ||||
|           tileProxy.Placement.InjectionData = injectedTile; | ||||
|           pathProxy.injectedTiles[tileProxy] = injectedTile; | ||||
|           pathProxy.tilesPendingInjection.RemoveAt(index); | ||||
|         } | ||||
|         return tilePlacementResult; | ||||
|       } | ||||
| 
 | ||||
|       return new TilePlacementResultProxy(TilePlacementResult.NoValidTile); | ||||
| 
 | ||||
|     private static Queue<DoorwayPair> GetDoorwayPairs(DungeonGenerator gen, TileProxy attachTo, IEnumerable<TileSet> useableTileSets, DungeonArchetype archetype, float normalizedDepth){ | ||||
|       IEnumerable<GameObjectChance> collection; | ||||
|       collection = useableTileSets.SelectMany(t => t.TileWeights.Weights); | ||||
|       return GetDoorwayPairs(gen, attachTo, collection, archetype, normalizedDepth); | ||||
|     } | ||||
| 
 | ||||
|     private static TilePlacementResultProxy TryPlaceTileResult(DungeonGenerator gen, BranchPathProxy pathProxy, DoorwayPair pair, DungeonArchetype archetype){ | ||||
|  |  | |||
|  | @ -191,6 +191,7 @@ namespace DunGenPlus.Generation { | |||
|       /* | ||||
|       Plugin.logger.LogError("Spawned"); | ||||
| 
 | ||||
| 
 | ||||
|       var colors = new Color[] { Color.red, Color.blue }; | ||||
| 
 | ||||
|       foreach(var tile in dungeonGenerator.CurrentDungeon.AllTiles){ | ||||
|  | @ -198,27 +199,27 @@ namespace DunGenPlus.Generation { | |||
| 
 | ||||
|         foreach(var globalProp in tile.GetComponentsInChildren<GlobalProp>()){ | ||||
|           if (globalProp.PropGroupID == 1717){ | ||||
|             var newGameObject = GameObject.Instantiate(DunGenPlusPanel.Instance.dungeonBoundsHelperGameObject); | ||||
|             newGameObject.transform.position = globalProp.transform.position + Vector3.up * 10f; | ||||
|             newGameObject.transform.localScale = Vector3.one * 4f; | ||||
|             Plugin.logger.LogError($"{globalProp.PropGroupID}: {newGameObject.transform.position}"); | ||||
|             //var newGameObject = GameObject.Instantiate(DunGenPlusPanel.Instance.dungeonBoundsHelperGameObject); | ||||
|             //newGameObject.transform.position = globalProp.transform.position; | ||||
|             //newGameObject.transform.localScale = Vector3.one * 1f; | ||||
|             Plugin.logger.LogError($"{globalProp.PropGroupID}: {globalProp.transform.position}"); | ||||
| 
 | ||||
|             var renderer = newGameObject.GetComponent<Renderer>(); | ||||
|             renderer.material.color = colors[0]; | ||||
|             //var renderer = newGameObject.GetComponent<Renderer>(); | ||||
|             //renderer.material.color = colors[0]; | ||||
| 
 | ||||
|             newGameObject.SetActive(true); | ||||
|            // newGameObject.SetActive(true); | ||||
|           } | ||||
| 
 | ||||
|           if (globalProp.PropGroupID == 1718){ | ||||
|             var newGameObject = GameObject.Instantiate(DunGenPlusPanel.Instance.dungeonBoundsHelperGameObject); | ||||
|             newGameObject.transform.position = globalProp.transform.position + Vector3.up * 10f; | ||||
|             newGameObject.transform.localScale = Vector3.one * 4f; | ||||
|             Plugin.logger.LogError($"{globalProp.PropGroupID}: {newGameObject.transform.position}"); | ||||
|             //var newGameObject = GameObject.Instantiate(DunGenPlusPanel.Instance.dungeonBoundsHelperGameObject); | ||||
|             //newGameObject.transform.position = globalProp.transform.position; | ||||
|             //newGameObject.transform.localScale = Vector3.one * 1f; | ||||
|             Plugin.logger.LogError($"{globalProp.PropGroupID}: {globalProp.transform.position}"); | ||||
| 
 | ||||
|             var renderer = newGameObject.GetComponent<Renderer>(); | ||||
|             renderer.material.color = colors[1]; | ||||
|             //var renderer = newGameObject.GetComponent<Renderer>(); | ||||
|             //renderer.material.color = colors[1]; | ||||
| 
 | ||||
|              newGameObject.SetActive(true); | ||||
|              //newGameObject.SetActive(true); | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -92,7 +92,7 @@ namespace DunGenPlus.Generation { | |||
|     } | ||||
| 
 | ||||
|     public static bool AllowRetryStop(bool defaultState){ | ||||
|       return defaultState || DevDebugManager.IsActive; | ||||
|       return defaultState || API.IsDevDebugModeActive(); | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ namespace DunGenPlus.Patches { | |||
|     [HarmonyPatch(typeof(DungeonGenerator), "InnerGenerate")] | ||||
|     public static void InnerGeneratePatch(ref DungeonGenerator __instance, bool isRetry, ref IEnumerator __result){ | ||||
|       //Plugin.logger.LogWarning($"InnerGenerate: {DunGenPlusGenerator.Active}, {DunGenPlusGenerator.ActiveAlternative}, {__instance.Status}"); | ||||
|       if (DevDebugManager.IsActive && !isRetry) { | ||||
|       if (API.IsDevDebugModeActive() && !isRetry) { | ||||
|         DevDebugManager.Instance.RecordNewSeed(__instance.ChosenSeed); | ||||
|       } | ||||
| 
 | ||||
|  | @ -382,6 +382,52 @@ namespace DunGenPlus.Patches { | |||
|     } | ||||
|      | ||||
| 
 | ||||
|     [HarmonyTranspiler] | ||||
|     [HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath", MethodType.Enumerator)] | ||||
|     public static IEnumerable<CodeInstruction> GenerateMainPathDebugPatch(IEnumerable<CodeInstruction> instructions){ | ||||
| 
 | ||||
|       var tileProxyNullSequence = new InstructionSequenceStandard("TileProxyNull"); | ||||
|       tileProxyNullSequence.AddBasic(OpCodes.Br); | ||||
|       tileProxyNullSequence.AddBasicLocal(OpCodes.Ldloc_S, 14); | ||||
|       tileProxyNullSequence.AddBasic(OpCodes.Brtrue); | ||||
| 
 | ||||
|       foreach(var instruction in instructions){ | ||||
|         if (tileProxyNullSequence.VerifyStage(instruction)) { | ||||
|           var specialFunction = typeof(DunGenPlusGenerator).GetMethod("PrintAddTileErrorQuick", BindingFlags.Static | BindingFlags.Public); | ||||
|           var field = typeof(DungeonGenerator).Assembly.GetType("DunGen.DungeonGenerator+<GenerateMainPath>d__100").GetField("<j>5__8", BindingFlags.NonPublic | BindingFlags.Instance); | ||||
| 
 | ||||
|           yield return instruction; | ||||
| 
 | ||||
|           yield return new CodeInstruction(OpCodes.Ldloc_1); | ||||
|           yield return new CodeInstruction(OpCodes.Ldarg_0); | ||||
|           yield return new CodeInstruction(OpCodes.Ldfld, field); | ||||
| 
 | ||||
|           yield return new CodeInstruction(OpCodes.Call, specialFunction); | ||||
| 
 | ||||
|           continue; | ||||
|         } | ||||
| 
 | ||||
|         yield return instruction; | ||||
|       } | ||||
| 
 | ||||
|       tileProxyNullSequence.ReportComplete(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static TileProxy lastAttachTo; | ||||
|     public static IEnumerable<TileSet> lastUseableTileSets; | ||||
|     public static float lastNormalizedDepth; | ||||
|     public static DungeonArchetype lastArchetype; | ||||
| 
 | ||||
|     [HarmonyPrefix] | ||||
|     [HarmonyPatch(typeof(DungeonGenerator), "AddTile")] | ||||
|     public static void AddTileDebugPatch(TileProxy attachTo, IEnumerable<TileSet> useableTileSets, float normalizedDepth, DungeonArchetype archetype){ | ||||
|       lastAttachTo = attachTo; | ||||
|       lastUseableTileSets = useableTileSets; | ||||
|       lastNormalizedDepth = normalizedDepth; | ||||
|       lastArchetype = archetype; | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|     [HarmonyTranspiler] | ||||
|     [HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath", MethodType.Enumerator)] | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue