From 99d9d7ae1e91989885e50d41b6eb91c94f6737d1 Mon Sep 17 00:00:00 2001 From: LadyAliceMargatroid Date: Tue, 10 Sep 2024 04:42:07 -0700 Subject: [PATCH] Added MainPathExtender --- .../DunGenExtenderPropertiesCollection.cs | 12 ++ DunGenPlus/DunGenPlus/DunGenExtender.cs | 2 +- DunGenPlus/DunGenPlus/DunGenPlus.csproj | 2 + .../Generation/DunGenPlusGenerationPaths.cs | 84 +++++++++++++ .../Generation/DunGenPlusGenerator.cs | 39 +++--- DunGenPlus/DunGenPlus/MainPathExtender.cs | 64 ++++++++++ .../Patches/DungeonGeneratorPatch.cs | 119 +++++++++++++++++- DunGenPlus/DunGenPlus/Plugin.cs | 2 +- 8 files changed, 299 insertions(+), 25 deletions(-) create mode 100644 DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerationPaths.cs create mode 100644 DunGenPlus/DunGenPlus/MainPathExtender.cs diff --git a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderPropertiesCollection.cs b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderPropertiesCollection.cs index 882d6cc..7c6b00c 100644 --- a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderPropertiesCollection.cs +++ b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderPropertiesCollection.cs @@ -15,6 +15,8 @@ namespace DunGenPlus.Collections { internal const string MainPathCountTooltip = "The number of main paths.\n\n1 means no additional main paths\n3 means two additional main paths\netc."; internal const string MainRoomTilePrefabTooltip = "The Tile prefab where the additional main paths will start from.\n\nCannot be null if MainPathCount is more than 1."; internal const string CopyNodeBehaviourTooltip = "Defines how the nodes list is copied onto the additional main paths.\n\nCopyFromMainPathPosition: nodes will copy based on the MainRoomTilePrefab's position in the main path.\nCopyFromNodeList: nodes will copy based on the MainRoomTilePrefab's position in the node list + 1."; + internal const string MainPathDetailsTooltip = "Tooltip"; + [Tooltip(MainPathCountTooltip)] [Range(1, 9)] @@ -23,11 +25,21 @@ namespace DunGenPlus.Collections { public GameObject MainRoomTilePrefab; [Tooltip(CopyNodeBehaviourTooltip)] public CopyNodeBehaviour CopyNodeBehaviour = CopyNodeBehaviour.CopyFromMainPathPosition; + [Tooltip(MainPathDetailsTooltip)] + public List MainPathDetails = new List(); + + public MainPathExtender GetMainPathDetails(int index) { + var count = MainPathDetails.Count; + if (count == 0) return null; + if (index < count) return MainPathDetails[index]; + return MainPathDetails[count - 1]; + } internal void CopyFrom(MainPathProperties props) { MainPathCount = props.MainPathCount; MainRoomTilePrefab = props.MainRoomTilePrefab; CopyNodeBehaviour = props.CopyNodeBehaviour; + MainPathDetails = props.MainPathDetails; } internal MainPathProperties Copy() { diff --git a/DunGenPlus/DunGenPlus/DunGenExtender.cs b/DunGenPlus/DunGenPlus/DunGenExtender.cs index 973742d..8566545 100644 --- a/DunGenPlus/DunGenPlus/DunGenExtender.cs +++ b/DunGenPlus/DunGenPlus/DunGenExtender.cs @@ -10,7 +10,7 @@ using DunGenPlus.Collections; namespace DunGenPlus { - [CreateAssetMenu(fileName = "DunGenExtender", menuName = "DunGenExtender", order = 1)] + [CreateAssetMenu(fileName = "DunGen Extender", menuName = "DunGenExtender/DunGen Extender", order = 1)] public class DunGenExtender : ScriptableObject { [Tooltip("DunGenExtender will only influence this DungeonFlow")] diff --git a/DunGenPlus/DunGenPlus/DunGenPlus.csproj b/DunGenPlus/DunGenPlus/DunGenPlus.csproj index 08b239e..97c7fdb 100644 --- a/DunGenPlus/DunGenPlus/DunGenPlus.csproj +++ b/DunGenPlus/DunGenPlus/DunGenPlus.csproj @@ -146,6 +146,8 @@ + + diff --git a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerationPaths.cs b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerationPaths.cs new file mode 100644 index 0000000..6ccbb2d --- /dev/null +++ b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerationPaths.cs @@ -0,0 +1,84 @@ +using DunGen; +using DunGen.Graph; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace DunGenPlus.Generation { + internal partial class DunGenPlusGenerator { + + public static MainPathExtender currentMainPathExtender; + + public static void SetCurrentMainPathExtender(int mainPathIndex){ + currentMainPathExtender = Properties.MainPathProperties.GetMainPathDetails(mainPathIndex); + } + + public static GraphLine GetLineAtDepth(DungeonFlow flow, float depth) { + if (!DunGenPlusGenerator.Active) { + //Plugin.logger.LogInfo("Default"); + return flow.GetLineAtDepth(depth); + } + + //Plugin.logger.LogInfo(currentMainPathExtender == null ? "NULL" : "ITEM"); + var lines = MainPathExtender.GetLines(currentMainPathExtender, flow); + return GetLineAtDepthHelper(lines, depth); + } + + public static GraphLine GetLineAtDepthHelper(List graphLines, float normalizedDepth) { + normalizedDepth = Mathf.Clamp(normalizedDepth, 0f, 1f); + if (normalizedDepth == 0f) return graphLines[0]; + if (normalizedDepth == 1f) return graphLines[graphLines.Count - 1]; + foreach (GraphLine graphLine in graphLines){ + if (normalizedDepth >= graphLine.Position && normalizedDepth < graphLine.Position + graphLine.Length) { + return graphLine; + } + } + Debug.LogError("GetLineAtDepth was unable to find a line at depth " + normalizedDepth.ToString() + ". This shouldn't happen."); + return null; + } + + public static List GetNodes(DungeonFlow flow){ + if (!DunGenPlusGenerator.Active) { + //Plugin.logger.LogInfo("Default"); + return flow.Nodes; + } + + //Plugin.logger.LogInfo(currentMainPathExtender == null ? "NULL" : "ITEM"); + return MainPathExtender.GetNodes(currentMainPathExtender, flow); + } + + public static BranchMode GetBranchMode(DungeonFlow flow) { + if (!DunGenPlusGenerator.Active) { + //Plugin.logger.LogInfo("Default M"); + return flow.BranchMode; + } + + //Plugin.logger.LogInfo(currentMainPathExtender == null ? "NULL" : "ITEM M"); + return MainPathExtender.GetBranchMode(currentMainPathExtender, flow); + } + + public static IntRange GetBranchCount(DungeonFlow flow) { + if (!DunGenPlusGenerator.Active) { + //Plugin.logger.LogInfo("Default C"); + return flow.BranchCount; + } + + //Plugin.logger.LogInfo(currentMainPathExtender == null ? "NULL" : "ITEM C"); + return MainPathExtender.GetBranchCount(currentMainPathExtender, flow); + } + + public static IntRange GetLength(DungeonFlow flow) { + if (!DunGenPlusGenerator.Active) { + Plugin.logger.LogInfo("Default"); + return flow.Length; + } + + Plugin.logger.LogInfo(currentMainPathExtender == null ? "NULL" : "ITEM"); + return MainPathExtender.GetLength(currentMainPathExtender, flow); + } + + } +} diff --git a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs index bb74a4f..66ccd78 100644 --- a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs +++ b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs @@ -142,42 +142,39 @@ namespace DunGenPlus.Generation { // index of MaxValue is how we tell which doorway proxy is fake var fakeDoorwayProxy = new DoorwayProxy(mainRoom, int.MaxValue, mainRoom.doorways[0].DoorwayComponent, Vector3.zero, Quaternion.identity); - // nodes - var nodesSorted = gen.DungeonFlow.Nodes.OrderBy(n => n.Position).ToList(); - var startingNodeIndexCache = -1; - if (copyNodeBehaviour == DunGenExtenderProperties.CopyNodeBehaviour.CopyFromNodeList) { - startingNodeIndexCache = nodesSorted.FindIndex(n => n.TileSets.SelectMany(t => t.TileWeights.Weights).Any(t => t.Value == mainRoomTilePrefab)); - - if (startingNodeIndexCache == -1) { - yield return gen.Wait(GenerateBranchPaths(gen, $"CopyNodeBehaviour being CopyFromNodeList AND MainRoomTilePrefab not existing in the Nodes' tilesets", LogLevel.Warning)); - yield break; - } - - startingNodeIndexCache++; - } - //FixDoorwaysToAllFloors(mainRoom, doorwayGroups); gen.ChangeStatus(GenerationStatus.MainPath); for (var b = 0; b < altCount; ++b) { + SetCurrentMainPathExtender(b + 1); RandomizeLineArchetypes(gen, true); + var previousTile = mainRoom; - var targetLength = Mathf.RoundToInt(gen.DungeonFlow.Length.GetRandom(gen.RandomStream) * gen.LengthMultiplier); + var targetLength = Mathf.RoundToInt(GetLength(gen.DungeonFlow).GetRandom(gen.RandomStream) * gen.LengthMultiplier); var archetypes = new List(targetLength); var newMainPathTiles = new List(); // this causes the main room to create three sets of branch paths // newMainPathTiles.Add(mainRoom); + // nodes + var nodesSorted = GetNodes(gen.DungeonFlow).OrderBy(n => n.Position).ToList(); int startingNodeIndex; - if (copyNodeBehaviour == DunGenExtenderProperties.CopyNodeBehaviour.CopyFromMainPathPosition) { + if (copyNodeBehaviour == DunGenExtenderProperties.CopyNodeBehaviour.CopyFromNodeList) { + var index = nodesSorted.FindIndex(n => n.TileSets.SelectMany(t => t.TileWeights.Weights).Any(t => t.Value == mainRoomTilePrefab)); + + if (index == -1) { + yield return gen.Wait(GenerateBranchPaths(gen, $"CopyNodeBehaviour being CopyFromNodeList AND MainRoomTilePrefab not existing in the Nodes' tilesets", LogLevel.Warning)); + yield break; + } + + startingNodeIndex = index + 1; + } else if (copyNodeBehaviour == DunGenExtenderProperties.CopyNodeBehaviour.CopyFromMainPathPosition) { var lineDepthRatio = Mathf.Clamp01(1f / (targetLength - 1)); startingNodeIndex = nodesSorted.FindIndex(n => n.Position >= lineDepthRatio); - } else if (copyNodeBehaviour == DunGenExtenderProperties.CopyNodeBehaviour.CopyFromNodeList) { - startingNodeIndex = startingNodeIndexCache; } else { - Plugin.logger.LogError($"{copyNodeBehaviour} is not yet defined. How did this happen?"); + Plugin.logger.LogFatal($"{copyNodeBehaviour} is not yet defined. Really really bad"); startingNodeIndex = -1; } @@ -188,7 +185,7 @@ namespace DunGenPlus.Generation { // and GenerateBranch() code for(var t = 1; t < targetLength; ++t){ var lineDepthRatio = Mathf.Clamp01((float)t / (targetLength - 1)); - var lineAtDepth = gen.DungeonFlow.GetLineAtDepth(lineDepthRatio); + var lineAtDepth = GetLineAtDepth(gen.DungeonFlow, lineDepthRatio); if (lineAtDepth == null){ yield return gen.Wait(gen.InnerGenerate(true)); yield break; @@ -281,6 +278,7 @@ namespace DunGenPlus.Generation { // this is major trickery and it works still for(var b = 0; b < altCount + 1; ++b){ Plugin.logger.LogDebug($"Branch {b}"); + SetCurrentMainPathExtender(b); RandomizeLineArchetypes(gen, false); gen.proxyDungeon.MainPathTiles = allMainPathTiles[b]; @@ -305,6 +303,7 @@ namespace DunGenPlus.Generation { Plugin.logger.Log(logLevel, $"Switching to default dungeon branch generation: {message}"); ActiveAlternative = false; + SetCurrentMainPathExtender(0); yield return gen.Wait(gen.GenerateBranchPaths()); ActiveAlternative = true; diff --git a/DunGenPlus/DunGenPlus/MainPathExtender.cs b/DunGenPlus/DunGenPlus/MainPathExtender.cs new file mode 100644 index 0000000..6e6d681 --- /dev/null +++ b/DunGenPlus/DunGenPlus/MainPathExtender.cs @@ -0,0 +1,64 @@ +using DunGen; +using DunGen.Graph; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace DunGenPlus { + + [System.Serializable] + public class PropertyOverride { + [Tooltip("If false, use the value found in DungeonFlow. If true, use this Value instead.")] + public bool Override; + public T Value; + + public PropertyOverride(bool _override, T value) { + Override = _override; + Value = value; + } + } + + [CreateAssetMenu(fileName = "Main Path Extender", menuName = "DunGenExtender/Main Path Extender", order = 2)] + public class MainPathExtender : ScriptableObject { + + public PropertyOverride Length = new PropertyOverride(false, new IntRange(5, 10)); + + public PropertyOverride BranchMode = new PropertyOverride(false, DunGen.BranchMode.Local); + public PropertyOverride BranchCount = new PropertyOverride(false, new IntRange(1, 5)); + + public PropertyOverride> Nodes = new PropertyOverride>(false, new List()); + public PropertyOverride> Lines = new PropertyOverride>(false, new List()); + + [Header("DEV ONLY: DON'T TOUCH")] + public string Version = "0"; + + public static IntRange GetLength(MainPathExtender extender, DungeonFlow flow) { + if (extender && extender.Length.Override) return extender.Length.Value; + return flow.Length; + } + + public static BranchMode GetBranchMode(MainPathExtender extender, DungeonFlow flow) { + if (extender && extender.BranchMode.Override) return extender.BranchMode.Value; + return flow.BranchMode; + } + + public static IntRange GetBranchCount(MainPathExtender extender, DungeonFlow flow) { + if (extender && extender.BranchCount.Override) return extender.BranchCount.Value; + return flow.BranchCount; + } + + public static List GetNodes(MainPathExtender extender, DungeonFlow flow) { + if (extender && extender.Nodes.Override) return extender.Nodes.Value; + return flow.Nodes; + } + + public static List GetLines(MainPathExtender extender, DungeonFlow flow) { + if (extender && extender.Lines.Override) return extender.Lines.Value; + return flow.Lines; + } + + } +} diff --git a/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs b/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs index b587e76..807539c 100644 --- a/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs +++ b/DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs @@ -13,6 +13,8 @@ using DunGenPlus.Generation; using DunGenPlus.Managers; using DunGenPlus.Collections; using DunGenPlus.DevTools; +using DunGen.Graph; +using UnityEngine; namespace DunGenPlus.Patches { internal class DungeonGeneratorPatch { @@ -23,6 +25,10 @@ namespace DunGenPlus.Patches { if (DevDebugManager.Instance && !isRetry) { DevDebugManager.Instance.RecordNewSeed(__instance.ChosenSeed); } + + if (DunGenPlusGenerator.Active && DunGenPlusGenerator.ActiveAlternative) { + DunGenPlusGenerator.SetCurrentMainPathExtender(0); + } } [HarmonyPostfix] @@ -42,9 +48,6 @@ namespace DunGenPlus.Patches { } - - - [HarmonyTranspiler] [HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath", MethodType.Enumerator)] public static IEnumerable GenerateMainPathPatch(IEnumerable instructions){ @@ -78,6 +81,116 @@ namespace DunGenPlus.Patches { archSequence.ReportComplete(); } + [HarmonyTranspiler] + [HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath", MethodType.Enumerator)] + public static IEnumerable GenerateMainPathGetLineAtDepthPatch(IEnumerable instructions){ + + var getLineFunction = typeof(DungeonFlow).GetMethod("GetLineAtDepth", BindingFlags.Instance | BindingFlags.Public); + var nodesField = typeof(DungeonFlow).GetField("Nodes", BindingFlags.Instance | BindingFlags.Public); + + var lineSequence = new InstructionSequenceStandard("GetLineAtDepth"); + lineSequence.AddBasic(OpCodes.Callvirt, getLineFunction); + + var nodesSequence = new InstructionSequenceStandard("Nodes", false); + nodesSequence.AddBasic(OpCodes.Ldfld, nodesField); + + foreach(var instruction in instructions){ + if (lineSequence.VerifyStage(instruction)) { + var specialFunction = typeof(DunGenPlusGenerator).GetMethod("GetLineAtDepth", BindingFlags.Static | BindingFlags.Public); + + yield return new CodeInstruction(OpCodes.Call, specialFunction); + + continue; + } + + if (nodesSequence.VerifyStage(instruction)) { + var specialFunction = typeof(DunGenPlusGenerator).GetMethod("GetNodes", BindingFlags.Static | BindingFlags.Public); + + yield return new CodeInstruction(OpCodes.Call, specialFunction); + + continue; + } + + + yield return instruction; + } + + lineSequence.ReportComplete(); + nodesSequence.ReportComplete(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(BranchCountHelper), "ComputeBranchCounts")] + public static IEnumerable ComputeBranchCountsPatch(IEnumerable instructions){ + + var branchModeField = typeof(DungeonFlow).GetField("BranchMode", BindingFlags.Instance | BindingFlags.Public); + + var branchSequence = new InstructionSequenceStandard("BranchMode", false); + branchSequence.AddBasic(OpCodes.Ldfld, branchModeField); + + foreach(var instruction in instructions){ + if (branchSequence.VerifyStage(instruction)) { + var specialFunction = typeof(DunGenPlusGenerator).GetMethod("GetBranchMode", BindingFlags.Static | BindingFlags.Public); + + yield return new CodeInstruction(OpCodes.Call, specialFunction); + + continue; + } + + yield return instruction; + } + + branchSequence.ReportComplete(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(BranchCountHelper), "ComputeBranchCountsGlobal")] + public static IEnumerable ComputeBranchCountsGlobalPatch(IEnumerable instructions){ + + var branchCountField = typeof(DungeonFlow).GetField("BranchCount", BindingFlags.Instance | BindingFlags.Public); + + var branchSequence = new InstructionSequenceStandard("BranchCount"); + branchSequence.AddBasic(OpCodes.Ldfld, branchCountField); + + foreach(var instruction in instructions){ + if (branchSequence.VerifyStage(instruction)) { + var specialFunction = typeof(DunGenPlusGenerator).GetMethod("GetBranchCount", BindingFlags.Static | BindingFlags.Public); + + yield return new CodeInstruction(OpCodes.Call, specialFunction); + + continue; + } + + yield return instruction; + } + + branchSequence.ReportComplete(); + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(DungeonGenerator), "InnerGenerate", MethodType.Enumerator)] + public static IEnumerable InnerGenerateLengthPatch(IEnumerable instructions){ + + var lengthField = typeof(DungeonFlow).GetField("Length", BindingFlags.Instance | BindingFlags.Public); + + var lengthSequence = new InstructionSequenceStandard("Length"); + lengthSequence.AddBasic(OpCodes.Ldfld, lengthField); + + foreach(var instruction in instructions){ + if (lengthSequence.VerifyStage(instruction)) { + var specialFunction = typeof(DunGenPlusGenerator).GetMethod("GetLength", BindingFlags.Static | BindingFlags.Public); + + yield return new CodeInstruction(OpCodes.Call, specialFunction); + + continue; + } + + yield return instruction; + } + + lengthSequence.ReportComplete(); + } + /* [HarmonyTranspiler] [HarmonyPatch(typeof(DungeonGenerator), "AddTile")] diff --git a/DunGenPlus/DunGenPlus/Plugin.cs b/DunGenPlus/DunGenPlus/Plugin.cs index 338d86d..4f4f326 100644 --- a/DunGenPlus/DunGenPlus/Plugin.cs +++ b/DunGenPlus/DunGenPlus/Plugin.cs @@ -25,7 +25,7 @@ namespace DunGenPlus { internal const string modGUID = "dev.ladyalice.dungenplus"; private const string modName = "Dungeon Generation Plus"; - private const string modVersion = "1.1.2"; + private const string modVersion = "1.2.0"; internal readonly Harmony Harmony = new Harmony(modGUID);