diff --git a/DunGenPlus/DunGenPlus/DunGenPlus.csproj b/DunGenPlus/DunGenPlus/DunGenPlus.csproj index 93d47c9..4d55dd1 100644 --- a/DunGenPlus/DunGenPlus/DunGenPlus.csproj +++ b/DunGenPlus/DunGenPlus/DunGenPlus.csproj @@ -168,6 +168,8 @@ + + diff --git a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs index 5f432ee..e6ceddc 100644 --- a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs +++ b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs @@ -276,7 +276,9 @@ namespace DunGenPlus.Generation { Plugin.logger.LogDebug($"Branch {b}"); RandomizeLineArchetypes(gen, false); gen.proxyDungeon.MainPathTiles = allMainPathTiles[b]; - yield return gen.Wait(gen.GenerateBranchPaths()); + + if (Properties.UseBranchLoopBoost) yield return gen.Wait(GenerateBranchBoostedPaths(gen)); + else yield return gen.Wait(gen.GenerateBranchPaths()); } ActiveAlternative = true; diff --git a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGeneratorBranchLoop.cs b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGeneratorBranchLoop.cs index be4662b..a3757f6 100644 --- a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGeneratorBranchLoop.cs +++ b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGeneratorBranchLoop.cs @@ -1,108 +1,262 @@ using DunGen; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using UnityEngine; -namespace DunGenPlus.Generation { +namespace DunGenPlus.Generation +{ internal partial class DunGenPlusGenerator { - public static (TilePlacementResult result, TileProxy tile) ProcessDoorwayPairs(DungeonGenerator gen, DungeonArchetype archetype, Queue doorwayPairs) { - if (Properties != null && Properties.UseBranchLoopBoost && gen.Status == GenerationStatus.Branching) { - return EncourageBranchPathLoopEncouragement(gen, doorwayPairs); - } + private class BranchPathProxy { + public TileProxy attachTileProxy; + public List list; + public float weight; - while(doorwayPairs.Count > 0) { - var pair = doorwayPairs.Dequeue(); - var result = gen.TryPlaceTile(pair, archetype, out var tileProxy); - if (result == TilePlacementResult.None) return (result, tileProxy); - gen.AddTilePlacementResult(result); + public Dictionary injectedTiles; + public List tilesPendingInjection; + + public BranchPathProxy(DungeonGenerator gen, TileProxy attachTileProxy){ + this.attachTileProxy = attachTileProxy; + list = new List(); + weight = 0f; + + injectedTiles = new Dictionary(gen.injectedTiles); + tilesPendingInjection = new List(gen.tilesPendingInjection); } - return (TilePlacementResult.NoValidTile, null); - } + public void CalculateWeight(DungeonGenerator gen){ + weight = list.Count * 0.25f; + var allDungeonDoorways = gen.proxyDungeon.AllTiles.SelectMany(t => t.Doorways); + foreach(var t in list) { + foreach(var d in allDungeonDoorways) { + foreach(var l in t.tileProxy.doorways) { + if (d.TileProxy == t.previousDoorway.TileProxy || d.TileProxy == attachTileProxy) continue; - public static (TilePlacementResult result, TileProxy tile) EncourageBranchPathLoopEncouragement(DungeonGenerator gen, Queue doorwayPairs) { - // get list of 5 potential targets - var validTiles = new List(); - while(doorwayPairs.Count > 0) { - var pair = doorwayPairs.Dequeue(); - var value = GetTileProxyOfDoorwayPair(gen, pair); - if (value.result == TilePlacementResult.None) { - validTiles.Add(value); - if (validTiles.Count >= Properties.BranchLoopBoostTileSearch) break; - } - } - - if (validTiles.Count == 0) { - return (TilePlacementResult.NoValidTile, null); - } - - // update their weight based on their potential doorway partners - var allDungeonDoorways = gen.proxyDungeon.AllTiles.SelectMany(t => t.Doorways); - //Plugin.logger.LogInfo("NEW TILES"); - foreach(var t in validTiles) { - var doorwayCount = 0; - foreach(var d in allDungeonDoorways) { - foreach(var l in t.tile.doorways) { - if (d.TileProxy == t.previousDoorway.TileProxy) continue; - if (gen.DungeonFlow.CanDoorwaysConnect(d.TileProxy.PrefabTile, l.TileProxy.PrefabTile, d.DoorwayComponent, l.DoorwayComponent) && Vector3.SqrMagnitude(d.Position - l.Position) < 1E-05) - doorwayCount++; + // favor paths that connect to other depths + if (gen.DungeonFlow.CanDoorwaysConnect(d.TileProxy.PrefabTile, l.TileProxy.PrefabTile, d.DoorwayComponent, l.DoorwayComponent) && Vector3.SqrMagnitude(d.Position - l.Position) < 1E-05){ + var diff = Mathf.Abs(d.TileProxy.Placement.PathDepth - l.TileProxy.Placement.PathDepth); + weight += diff; + } + } } } - - if (doorwayCount > 0) { - //Plugin.logger.LogInfo($"{t.weight} -> {t.weight * (1f + doorwayCount * Properties.BranchLoopBoostTileScale)} ({doorwayCount})"); - t.weight *= (1f + doorwayCount * Properties.BranchLoopBoostTileScale); - } else { - //Plugin.logger.LogInfo($"{t.weight}"); - } } - var bestChoice = validTiles.OrderByDescending(t => t.weight).FirstOrDefault(); - //Plugin.logger.LogInfo($"Best: {bestChoice.weight}"); - - MakeTileProxyConnection(gen, bestChoice); - gen.AddTilePlacementResult(bestChoice.result); - - return (bestChoice.result, bestChoice.tile); } private class TilePlacementResultProxy { public TilePlacementResult result; - public TileProxy tile; + public TileProxy tileProxy; public DoorwayProxy previousDoorway; public DoorwayProxy nextDoorway; - public float weight; public TilePlacementResultProxy(TilePlacementResult result) { this.result = result; - tile = null; + tileProxy = null; previousDoorway = null; nextDoorway = null; - weight = 0f; } - public TilePlacementResultProxy(TilePlacementResult result, TileProxy tile, DoorwayProxy previousDoorway, DoorwayProxy nextDoorway, float weight) { + public TilePlacementResultProxy(TilePlacementResult result, TileProxy tile, DoorwayProxy previousDoorway, DoorwayProxy nextDoorway) { this.result = result; - this.tile = tile; + this.tileProxy = tile; this.previousDoorway = previousDoorway; this.nextDoorway = nextDoorway; - this.weight = weight; } } - private static TilePlacementResultProxy GetTileProxyOfDoorwayPair(DungeonGenerator gen, DoorwayPair pair){ + public static IEnumerator GenerateBranchBoostedPaths(DungeonGenerator gen){ + gen.ChangeStatus(GenerationStatus.Branching); + var mainPathBranches = new int[gen.proxyDungeon.MainPathTiles.Count]; + BranchCountHelper.ComputeBranchCounts(gen.DungeonFlow, gen.RandomStream, gen.proxyDungeon, ref mainPathBranches); + + // do all nodes on main path + for(var b = 0; b < mainPathBranches.Length; ++b){ + var tile = gen.proxyDungeon.MainPathTiles[b]; + var branchCount = mainPathBranches[b]; + + // skip if not branchable tile + if (tile.Placement.Archetype == null || branchCount == 0) { + continue; + } + + // the amount of branches per tile + for(var i = 0; i < branchCount; ++i){ + // create a bunch of proxy paths + // we evaulate later on the best one + var pathProxys = new List(); + for(var x = 0; x < 5; ++x){ + var currentPathProxy = new BranchPathProxy(gen, tile); + var previousTile = tile; + var branchDepth = tile.Placement.Archetype.BranchingDepth.GetRandom(gen.RandomStream); + for(var depth = 0; depth < branchDepth; ++depth){ + + // get tilesets, branch cap or regular + List useableTileSets; + if (depth == branchDepth - 1 && tile.Placement.Archetype.GetHasValidBranchCapTiles()){ + if (tile.Placement.Archetype.BranchCapType == BranchCapType.InsteadOf) { + useableTileSets = tile.Placement.Archetype.BranchCapTileSets; + } else { + useableTileSets = tile.Placement.Archetype.TileSets.Concat(tile.Placement.Archetype.BranchCapTileSets).ToList(); + } + } else { + useableTileSets = tile.Placement.Archetype.TileSets; + } + + // get potential tile to add + var normalizedDepth = (branchDepth <= 1) ? 1f : (float)depth / (branchDepth - 1); + var tileResult = GetTileResult(gen, currentPathProxy, previousTile, useableTileSets, normalizedDepth, tile.Placement.Archetype); + var tileProxy = tileResult.tileProxy; + if (tileProxy == null) { + // it's over, we done + break; + } + + // add + currentPathProxy.list.Add(tileResult); + tileProxy.Placement.BranchDepth = depth; + tileProxy.Placement.NormalizedBranchDepth = normalizedDepth; + previousTile = tileProxy; + } + + // we can't save this path + if (currentPathProxy.list.Count == 0) break; + + // record path + currentPathProxy.CalculateWeight(gen); + pathProxys.Add(currentPathProxy); + } + + // time to evaulate best path then add + var bestPath = pathProxys.OrderByDescending(p => p.weight).FirstOrDefault(); + if (bestPath != null) { + foreach(var item in bestPath.list){ + MakeTileProxyConnection(gen, item); + + item.tileProxy.Placement.GraphNode = item.previousDoorway.TileProxy.Placement.GraphNode; + item.tileProxy.Placement.GraphLine = item.previousDoorway.TileProxy.Placement.GraphLine; + } + + gen.injectedTiles = bestPath.injectedTiles; + gen.tilesPendingInjection = bestPath.tilesPendingInjection; + + if (gen.ShouldSkipFrame(true)){ + yield return gen.GetRoomPause(); + } + } + } + + // why null? + tile = null; + } + + yield break; + + } + + private static TilePlacementResultProxy GetTileResult(DungeonGenerator gen, BranchPathProxy pathProxy, TileProxy attachTo, IEnumerable useableTileSets, float normalizedDepth, DungeonArchetype archetype){ + // get tile injection + InjectedTile injectedTile = null; + var index = -1; + if (pathProxy.tilesPendingInjection != null) { + var pathDepth = (float)attachTo.Placement.PathDepth / gen.targetLength - 1f; + var branchDepth = normalizedDepth; + for(var i = 0; i < pathProxy.tilesPendingInjection.Count; ++i){ + var injectedTile2 = pathProxy.tilesPendingInjection[i]; + if (injectedTile2.ShouldInjectTileAtPoint(false, pathDepth, branchDepth)) { + injectedTile = injectedTile2; + index = i; + break; + } + } + } + + // get tiles to consider + IEnumerable collection; + if (injectedTile != null) { + collection = new List(injectedTile.TileSet.TileWeights.Weights); + } else { + collection = useableTileSets.SelectMany(t => t.TileWeights.Weights); + } + + // get rotation state + var value = attachTo.PrefabTile.AllowRotation; + if (gen.OverrideAllowTileRotation) { + value = gen.AllowTileRotation; + } + + var doorwayPairFinder = new DoorwayPairFinder(); + doorwayPairFinder.DungeonFlow = gen.DungeonFlow; + doorwayPairFinder.RandomStream = gen.RandomStream; + doorwayPairFinder.Archetype = archetype; + doorwayPairFinder.GetTileTemplateDelegate = new GetTileTemplateDelegate(gen.GetTileTemplate); + doorwayPairFinder.IsOnMainPath = false; + doorwayPairFinder.NormalizedDepth = normalizedDepth; + doorwayPairFinder.PreviousTile = attachTo; + doorwayPairFinder.UpVector = gen.UpVector; + doorwayPairFinder.AllowRotation = new bool?(value); + doorwayPairFinder.TileWeights = new List(collection); + doorwayPairFinder.IsTileAllowedPredicate = (TileProxy prev, TileProxy next, ref float weight) => { + var flag4 = prev != null && next.Prefab == prev.Prefab; + var tileRepeatMode = TileRepeatMode.Allow; + if (gen.OverrideRepeatMode) { + tileRepeatMode = gen.RepeatMode; + } else if (next != null) { + tileRepeatMode = next.PrefabTile.RepeatMode; + } + bool result2; + switch(tileRepeatMode) { + case TileRepeatMode.Allow: + result2 = true; + break; + case TileRepeatMode.DisallowImmediate: + result2 = !flag4; + break; + case TileRepeatMode.Disallow: + result2 = !gen.proxyDungeon.AllTiles.Where(x => x.Prefab == next.Prefab).Any(); + break; + default: + throw new NotImplementedException($"TileRepeatMode {tileRepeatMode} is not implemented"); + } + return result2; + }; + + var maxCount = gen.UseMaximumPairingAttempts ? new int?(gen.MaxPairingAttempts) : null; + var doorwayPairs = doorwayPairFinder.GetDoorwayPairs(maxCount); + 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 TilePlacementResultProxy TryPlaceTileResult(DungeonGenerator gen, BranchPathProxy pathProxy, DoorwayPair pair, DungeonArchetype archetype){ var nextTemplate = pair.NextTemplate; var previousDoorway = pair.PreviousDoorway; if (nextTemplate == null) return new TilePlacementResultProxy(TilePlacementResult.TemplateIsNull); var index = pair.NextTemplate.Doorways.IndexOf(pair.NextDoorway); + var tile = new TileProxy(nextTemplate); - tile.Placement.isOnMainPath = false; + tile.Placement.IsOnMainPath = false; + tile.Placement.Archetype = archetype; tile.Placement.TileSet = pair.NextTileSet; if (previousDoorway != null) { @@ -110,7 +264,7 @@ namespace DunGenPlus.Generation { tile.PositionBySocket(myDoorway, previousDoorway); var bounds = tile.Placement.Bounds; if (gen.RestrictDungeonToBounds && !gen.TilePlacementBounds.Contains(bounds)) return new TilePlacementResultProxy(TilePlacementResult.OutOfBounds); - if (gen.IsCollidingWithAnyTile(tile, previousDoorway.TileProxy)) return new TilePlacementResultProxy(TilePlacementResult.TileIsColliding); + if (IsCollidingWithAnyTileAndTileResult(gen, pathProxy, tile, previousDoorway.TileProxy)) return new TilePlacementResultProxy(TilePlacementResult.TileIsColliding); } if (tile == null) return new TilePlacementResultProxy(TilePlacementResult.NewTileIsNull); @@ -118,14 +272,25 @@ namespace DunGenPlus.Generation { tile.Placement.PathDepth = pair.PreviousTile.Placement.PathDepth; tile.Placement.BranchDepth = pair.PreviousTile.Placement.IsOnMainPath ? 0 : (pair.PreviousTile.Placement.BranchDepth + 1); - return new TilePlacementResultProxy(TilePlacementResult.None, tile, previousDoorway, tile.Doorways[index], pair.TileWeight); + return new TilePlacementResultProxy(TilePlacementResult.None, tile, previousDoorway, tile.Doorways[index]); + } + + private static bool IsCollidingWithAnyTileAndTileResult(DungeonGenerator gen, BranchPathProxy pathProxy, TileProxy newTile, TileProxy previousTile){ + foreach(var tileproxy in gen.proxyDungeon.AllTiles.Concat(pathProxy.list.Select(t => t.tileProxy))){ + var flag = previousTile == tileproxy; + var maxOverlap = flag ? gen.OverlapThreshold : (-gen.Padding); + if (gen.DisallowOverhangs && !flag){ + if (newTile.IsOverlappingOrOverhanging(tileproxy, gen.UpDirection, maxOverlap)) return true; + } else if (newTile.IsOverlapping(tileproxy, maxOverlap)) return true; + } + return false; } private static void MakeTileProxyConnection(DungeonGenerator gen, TilePlacementResultProxy proxy) { if (proxy.previousDoorway != null) { gen.proxyDungeon.MakeConnection(proxy.previousDoorway, proxy.nextDoorway); } - gen.proxyDungeon.AddTile(proxy.tile); + gen.proxyDungeon.AddTile(proxy.tileProxy); } } diff --git a/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs b/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs index 2d1cfdc..b587e76 100644 --- a/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs +++ b/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs @@ -78,6 +78,7 @@ namespace DunGenPlus.Patches { archSequence.ReportComplete(); } + /* [HarmonyTranspiler] [HarmonyPatch(typeof(DungeonGenerator), "AddTile")] public static IEnumerable AddTilePatch(IEnumerable instructions, ILGenerator generator) { @@ -145,6 +146,8 @@ namespace DunGenPlus.Patches { } + */ + [HarmonyPostfix] [HarmonyPatch(typeof(RoundManager), "FinishGeneratingLevel")] public static void GenerateBranchPathsPatch(){