From aeb8ea576ed01b70e69409ee230b87e01d9bcf1c Mon Sep 17 00:00:00 2001 From: LadyAliceMargatroid Date: Fri, 2 Aug 2024 03:55:36 -0700 Subject: [PATCH] Added API to create extender asset Added RandomGuaranteedScrapSpawn component --- DunGenPlus/DunGenPlus/API.cs | 14 +++- .../Collections/DunGenExtenderProperties.cs | 6 +- .../DunGenPlus/Collections/NodeArchetype.cs | 2 +- .../Scrap/RandomGuaranteedScrapSpawn.cs | 54 ++++++++++++++ DunGenPlus/DunGenPlus/DunGenExtender.cs | 4 +- DunGenPlus/DunGenPlus/DunGenPlus.csproj | 11 +++ .../Generation/DunGenPlusGenerator.cs | 1 + .../DunGenPlus/Patches/RoundManagerPatch.cs | 40 +++++++++++ .../DunGenPlus/Patches/StartOfRoundPatch.cs | 70 +++++++++++++++++++ DunGenPlus/DunGenPlus/Plugin.cs | 3 + README.md | 4 ++ 11 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 DunGenPlus/DunGenPlus/Components/Scrap/RandomGuaranteedScrapSpawn.cs create mode 100644 DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs create mode 100644 DunGenPlus/DunGenPlus/Patches/StartOfRoundPatch.cs diff --git a/DunGenPlus/DunGenPlus/API.cs b/DunGenPlus/DunGenPlus/API.cs index b2eadb9..20b5d98 100644 --- a/DunGenPlus/DunGenPlus/API.cs +++ b/DunGenPlus/DunGenPlus/API.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using DunGen; using DunGen.Graph; +using UnityEngine; namespace DunGenPlus { @@ -16,7 +17,7 @@ namespace DunGenPlus return false; } - if (Plugin.DunGenExtenders.ContainsKey(dungeonFlow)) { + if (ContainsDungeonFlow(dungeonFlow)) { Plugin.logger.LogWarning($"Already contains DunGenExtender asset for {dungeonFlow.name}"); return false; } @@ -36,5 +37,16 @@ namespace DunGenPlus return AddDunGenExtender(dunGenExtender.DungeonFlow, dunGenExtender); } + public static bool ContainsDungeonFlow(DungeonFlow dungeonFlow) { + return Plugin.DunGenExtenders.ContainsKey(dungeonFlow); + } + + public static DunGenExtender CreateDunGenExtender(DungeonFlow dungeonFlow){ + var extender = ScriptableObject.CreateInstance(); + extender.DungeonFlow = dungeonFlow; + + return extender; + } + } } diff --git a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs index 47fab81..1b82376 100644 --- a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs +++ b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs @@ -42,7 +42,7 @@ namespace DunGenPlus.Collections { [Header("Archetypes on Normal Nodes")] [Tooltip("If enabled, adds archetypes to the normal nodes in the DungeonFlow.\n\nBy default, nodes cannot have branching paths since they don't have archetype references. This allows nodes to have branching paths.")] public bool AddArchetypesToNormalNodes = false; - public List NormalNodeArchetypes; + public List NormalNodeArchetypes = new List(); internal Dictionary _normalNodeArchetypesDictioanry; internal NodeArchetype _defaultNodeArchetype; @@ -54,9 +54,9 @@ namespace DunGenPlus.Collections { [Tooltip("If enabled, every archetype in LineRandomizerArchetypes will have the last LineRandomizerTakeCount tilesets replaced by a randomly selected set of tilesets from LineRandomizerTileSets. This applies for both archetype's TileSets and BranchCapTileSets.\n\nThis is designed for the scenario where dungeon generation takes a long time due to the combination of too many tiles and/or doorways in those tiles. This can reduce dungeon generation time while keeping some of the randomness of dungeon generation.\n\nAs stated previously, this WILL replace the last LineRandomizerTakeCount tilesets in the archetype's TileSets and BranchCapTileSets. As such you must guarantee that those elements can be replaced.")] public bool UseLineRandomizer = false; [Tooltip("The archetypes whose tilesets will be replaced.\n\nThese archetypes should ideally used in the Lines section of DungeonFlow, but it's a free country.")] - public List LineRandomizerArchetypes; + public List LineRandomizerArchetypes = new List(); [Tooltip("The tilesets that will be used for replacement.")] - public List LineRandomizerTileSets; + public List LineRandomizerTileSets = new List(); [Tooltip("The amount of tilesets that will be replaced from the archetypes, starting from the last element to the first element.\n\nAs stated previously, this WILL replace the tilesets in the archetype's TileSets and BranchCapTileSets. As such you must guarantee that those elements can be replaced.")] public int LineRandomizerTakeCount = 3; diff --git a/DunGenPlus/DunGenPlus/Collections/NodeArchetype.cs b/DunGenPlus/DunGenPlus/Collections/NodeArchetype.cs index a3a8eb1..e54f3b3 100644 --- a/DunGenPlus/DunGenPlus/Collections/NodeArchetype.cs +++ b/DunGenPlus/DunGenPlus/Collections/NodeArchetype.cs @@ -13,7 +13,7 @@ namespace DunGenPlus.Collections { [Tooltip("The normal node with this label will gain a randomly chosen archetype.\n\nIf empty, this becomes the default choice for any normal node without a NodeArchetype specified in this list.")] public string label; [Tooltip("The list of archetypes. One will be randomly chosen.")] - public List archetypes; + public List archetypes = new List(); } } diff --git a/DunGenPlus/DunGenPlus/Components/Scrap/RandomGuaranteedScrapSpawn.cs b/DunGenPlus/DunGenPlus/Components/Scrap/RandomGuaranteedScrapSpawn.cs new file mode 100644 index 0000000..fb2e9b3 --- /dev/null +++ b/DunGenPlus/DunGenPlus/Components/Scrap/RandomGuaranteedScrapSpawn.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Unity.Netcode; + +namespace DunGenPlus.Components.Scrap { + public class RandomGuaranteedScrapSpawn : MonoBehaviour { + + + public float spawnChance = 1f; + public int minimumScrapValue = 0; + + public static Dictionary> scrapItemCache; + + public static void ResetCache(){ + scrapItemCache = new Dictionary>(); + } + + public static IEnumerable GetCachedItemList(List allMoonItems, int scrapValue) { + if (!scrapItemCache.TryGetValue(scrapValue, out var list)){ + list = allMoonItems.Select(i => i.spawnableItem).Where(i => i.minValue >= scrapValue).ToArray(); + scrapItemCache.Add(scrapValue, list); + } + return list; + } + + public (NetworkObject itemReference, int scrapValue) CreateItem(RoundManager roundManager, List allMoonItems){ + var anomalyRandom = roundManager.AnomalyRandom; + if (anomalyRandom.NextDouble() >= spawnChance) return (null, 0); + + var itemList = GetCachedItemList(allMoonItems, minimumScrapValue); + var itemListCount = itemList.Count(); + if (itemListCount == 0) return (null, 0); + + var randomItem = itemList.ElementAt(anomalyRandom.Next(itemListCount)); + var randomValue = (int)(anomalyRandom.Next(randomItem.minValue, randomItem.maxValue) * roundManager.scrapValueMultiplier); + + var gameObject = Instantiate(randomItem.spawnPrefab, transform.position, Quaternion.identity, roundManager.spawnedScrapContainer); + var itemComp = gameObject.GetComponent(); + + gameObject.transform.rotation = Quaternion.Euler(randomItem.restingRotation); + itemComp.fallTime = 0f; + itemComp.scrapValue = randomValue; + + var networkComp = gameObject.GetComponent(); + networkComp.Spawn(false); + return (networkComp, randomValue); + } + + } +} diff --git a/DunGenPlus/DunGenPlus/DunGenExtender.cs b/DunGenPlus/DunGenPlus/DunGenExtender.cs index 6b9c69f..a352dec 100644 --- a/DunGenPlus/DunGenPlus/DunGenExtender.cs +++ b/DunGenPlus/DunGenPlus/DunGenExtender.cs @@ -15,8 +15,8 @@ namespace DunGenPlus { [Tooltip("DunGenExtender will only influence this DungeonFlow")] public DungeonFlow DungeonFlow; - public DunGenExtenderProperties Properties; - public DunGenExtenderEvents Events; + public DunGenExtenderProperties Properties = new DunGenExtenderProperties(); + public DunGenExtenderEvents Events = new DunGenExtenderEvents(); [Header("DEV ONLY: DON'T TOUCH")] public string Version = "0"; diff --git a/DunGenPlus/DunGenPlus/DunGenPlus.csproj b/DunGenPlus/DunGenPlus/DunGenPlus.csproj index b1a1465..a1d68b1 100644 --- a/DunGenPlus/DunGenPlus/DunGenPlus.csproj +++ b/DunGenPlus/DunGenPlus/DunGenPlus.csproj @@ -60,6 +60,14 @@ ..\..\..\Libraries\Unity.Collections.dll + + False + ..\..\..\Libraries\Unity.Netcode.Components.dll + + + False + ..\..\..\Libraries\Unity.Netcode.Runtime.dll + False ..\..\..\Libraries\Unity.RenderPipelines.Core.Runtime.dll @@ -99,6 +107,7 @@ + @@ -106,6 +115,8 @@ + + diff --git a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs index fb5fb7c..7df7927 100644 --- a/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs +++ b/DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs @@ -39,6 +39,7 @@ namespace DunGenPlus.Generation { Properties = props; if (Properties.UseDungeonBounds) { + generator.DebugRender = true; generator.RestrictDungeonToBounds = Properties.UseDungeonBounds; var bounds = Properties.GetDungeonBounds(generator.LengthMultiplier); generator.TilePlacementBounds = bounds; diff --git a/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs b/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs new file mode 100644 index 0000000..02d80c1 --- /dev/null +++ b/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs @@ -0,0 +1,40 @@ +using DunGen; +using DunGenPlus.Components.Scrap; +using DunGenPlus.Generation; +using HarmonyLib; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Unity.Netcode; + +namespace DunGenPlus.Patches { + public class RoundManagerPatch { + + [HarmonyPrefix] + [HarmonyPatch(typeof(RoundManager), "waitForScrapToSpawnToSync")] + public static void waitForScrapToSpawnToSyncPatch (ref RoundManager __instance, ref NetworkObjectReference[] spawnedScrap, ref int[] scrapValues) { + if (DunGenPlusGenerator.Active) { + var spawnedScrapList = spawnedScrap.ToList(); + var scrapValuesList = scrapValues.ToList(); + + var sources = UnityEngine.Object.FindObjectsOfType(); + RandomGuaranteedScrapSpawn.ResetCache(); + foreach(var s in sources) { + var result = s.CreateItem(__instance, __instance.currentLevel.spawnableScrap); + if (result.itemReference != null) { + Plugin.logger.LogInfo($"Created guaranteed item {result.itemReference.gameObject.name} w/ value {result.scrapValue}"); + spawnedScrapList.Add(result.itemReference); + scrapValuesList.Add(result.scrapValue); + } + } + + spawnedScrap = spawnedScrapList.ToArray(); + scrapValues = scrapValuesList.ToArray(); + } + } + + } +} diff --git a/DunGenPlus/DunGenPlus/Patches/StartOfRoundPatch.cs b/DunGenPlus/DunGenPlus/Patches/StartOfRoundPatch.cs new file mode 100644 index 0000000..1877208 --- /dev/null +++ b/DunGenPlus/DunGenPlus/Patches/StartOfRoundPatch.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using HarmonyLib; + +namespace DunGenPlus.Patches { + + // like could be it's own mod + // but I don't wanna be the guy who messes with the original dungeon flows + // and break someone else's mod cause of X edge case + // and have to deal with that + // nah bruh, let someone else deal with it + + internal class StartOfRoundPatch { + + public static readonly string[] validDungeonFlowTargets = new [] { + "Level1Flow", "Level2Flow", "Level1FlowExtraLarge", "Level1Flow3Exits" + }; + + public static readonly Dictionary validStartTileTargets = new Dictionary() { + { "StartRoom", 2 }, + { "ManorStartRoom", 3 } + }; + + [HarmonyPatch(typeof(RoundManager), "Awake")] + [HarmonyPrefix] + public static void AwakePatch(ref RoundManager __instance){ + var dungeonFlows = __instance.dungeonFlowTypes.Select(d => d.dungeonFlow); + foreach(var d in dungeonFlows) { + if (!validDungeonFlowTargets.Contains(d.name)) continue; + if (API.ContainsDungeonFlow(d)) continue; + + Plugin.logger.LogInfo($"Creating DunGenExtender for {d.name}"); + + var tiles = d.Lines + .Select(i => i.DungeonArchetypes) + .SelectMany(i => i) + .Select(i => i.TileSets) + .SelectMany(i => i) + .Select(i => i.TileWeights.Weights) + .SelectMany(i => i) + .Select(i => i.Value); + foreach(var t in tiles) { + if (validStartTileTargets.TryGetValue(t.name, out var paths)) { + var extender = API.CreateDunGenExtender(d); + var props = extender.Properties; + props.MainPathCount = paths; + props.MainRoomTilePrefab = t; + + d.Length = new DunGen.IntRange(d.Length.Min / 2, d.Length.Max / 2); + Plugin.logger.LogInfo($"New length: {d.Length}"); + + if (t.name == "StartRoom") { + var lines = d.Lines; + lines[0].Length = 0.2f; + lines[1].Length -= 0.2f - lines[1].Position; + lines[1].Position = 0.2f; + } + + API.AddDunGenExtender(extender); + break; + } + } + } + } + + } +} diff --git a/DunGenPlus/DunGenPlus/Plugin.cs b/DunGenPlus/DunGenPlus/Plugin.cs index cb761d3..4c165f5 100644 --- a/DunGenPlus/DunGenPlus/Plugin.cs +++ b/DunGenPlus/DunGenPlus/Plugin.cs @@ -42,6 +42,9 @@ namespace DunGenPlus { Harmony.PatchAll(typeof(DungeonGeneratorPatch)); Harmony.PatchAll(typeof(DoorwayConnectionPatch)); + Harmony.PatchAll(typeof(RoundManagerPatch)); + + //Harmony.PatchAll(typeof(StartOfRoundPatch)); Assets.LoadAssets(); DungeonManager.GlobalDungeonEvents.onBeforeDungeonGenerate.AddListener(OnDunGenExtenderLoad); diff --git a/README.md b/README.md index 420f779..fc05a45 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ Please refer to the [wiki](https://git.touhou.dev/Raphtalia/DungeonGenerationPlu The prominent feature of this API is the ability to seemlesly add multiple main paths to your interior. Below are some examples. +![](https://i.imgur.com/XvygIZx.png) + +![](https://i.imgur.com/GKZVqOa.png) + ![](https://i.imgur.com/nN5Zz5e.png) ![](https://i.imgur.com/ogrUKAI.png)