Updated for LLL 1.4.0

Added DetailedGlobalPropSettings
This commit is contained in:
LadyAliceMargatroid 2025-01-17 09:26:39 -08:00
parent fec9e35898
commit 16d1c2d056
9 changed files with 89 additions and 56 deletions

View File

@ -0,0 +1,27 @@
using DunGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Collections {
[System.Serializable]
public class DetailedGlobalPropSettings {
internal const string MinimumDistanceTooltip = "The minimum distance between each Global Props of this id.";
public int ID;
[Space]
[Tooltip(MinimumDistanceTooltip)]
public float MinimumDistance;
public DetailedGlobalPropSettings(int id, float minimumDistance) {
ID = id;
MinimumDistance = minimumDistance;
}
}
}

View File

@ -6,7 +6,6 @@ using System.Threading.Tasks;
using static DunGenPlus.Collections.DunGenExtenderProperties; using static DunGenPlus.Collections.DunGenExtenderProperties;
using UnityEngine; using UnityEngine;
using DunGen; using DunGen;
using DunGen.Graph;
namespace DunGenPlus.Collections { namespace DunGenPlus.Collections {
@ -17,7 +16,7 @@ namespace DunGenPlus.Collections {
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 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 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 = "Overrides certain DungeonFlow values during the main path generation.\n\nThe order of items in this list correspond to the order of the main paths being generated.\nThe first item in this list will activate for the first main path, the second item for the second main path, and so on. If there are more main paths than items in this list, the last item is used instead."; internal const string MainPathDetailsTooltip = "Overrides certain DungeonFlow values during the main path generation.\n\nThe order of items in this list correspond to the order of the main paths being generated.\nThe first item in this list will activate for the first main path, the second item for the second main path, and so on. If there are more main paths than items in this list, the last item is used instead.";
internal const string DetailedGlobalPropSettingsTooltip = "Additional settings for how Global Props spawn.";
[Tooltip(MainPathCountTooltip)] [Tooltip(MainPathCountTooltip)]
[Range(1, 9)] [Range(1, 9)]
@ -28,6 +27,8 @@ namespace DunGenPlus.Collections {
public CopyNodeBehaviour CopyNodeBehaviour = CopyNodeBehaviour.CopyFromMainPathPosition; public CopyNodeBehaviour CopyNodeBehaviour = CopyNodeBehaviour.CopyFromMainPathPosition;
[Tooltip(MainPathDetailsTooltip)] [Tooltip(MainPathDetailsTooltip)]
public List<MainPathExtender> MainPathDetails = new List<MainPathExtender>(); public List<MainPathExtender> MainPathDetails = new List<MainPathExtender>();
[Tooltip(DetailedGlobalPropSettingsTooltip)]
public List<DetailedGlobalPropSettings> DetailedGlobalPropSettings = new List<DetailedGlobalPropSettings>();
public MainPathExtender GetMainPathDetails(int index) { public MainPathExtender GetMainPathDetails(int index) {
var count = MainPathDetails.Count; var count = MainPathDetails.Count;
@ -41,6 +42,7 @@ namespace DunGenPlus.Collections {
MainRoomTilePrefab = props.MainRoomTilePrefab; MainRoomTilePrefab = props.MainRoomTilePrefab;
CopyNodeBehaviour = props.CopyNodeBehaviour; CopyNodeBehaviour = props.CopyNodeBehaviour;
MainPathDetails = props.MainPathDetails; MainPathDetails = props.MainPathDetails;
DetailedGlobalPropSettings = props.DetailedGlobalPropSettings;
} }
internal MainPathProperties Copy() { internal MainPathProperties Copy() {

View File

@ -11,13 +11,16 @@ namespace DunGenPlus.Collections {
public class LocalGlobalPropSettings { public class LocalGlobalPropSettings {
internal const string GlobalPropLimitTooltip = "If true, when PostProcess reaches the local limit of Global Props for all main paths but does not reach the global limit, use the remaining props in this main path to reach the global limit."; internal const string GlobalPropLimitTooltip = "If true, when PostProcess reaches the local limit of Global Props for all main paths but does not reach the global limit, use the remaining props in this main path to reach the global limit.";
internal const string MinimumDistanceBetweenPropsTooltip = "If true, Global Props of this id MUST have a minimum distance between each other.";
public int ID; public int ID;
[Space]
public IntRange Count; public IntRange Count;
[Tooltip(GlobalPropLimitTooltip)] [Tooltip(GlobalPropLimitTooltip)]
public bool UseToReachGlobalPropLimit; public bool UseToReachGlobalPropLimit;
public LocalGlobalPropSettings(int id, IntRange count, bool useToReachGlobalPropLimit) { public LocalGlobalPropSettings(int id, IntRange count, bool useToReachGlobalPropLimit = false) {
ID = id; ID = id;
Count = count; Count = count;
UseToReachGlobalPropLimit = useToReachGlobalPropLimit; UseToReachGlobalPropLimit = useToReachGlobalPropLimit;

View File

@ -137,6 +137,7 @@
<Compile Include="API.cs" /> <Compile Include="API.cs" />
<Compile Include="Assets.cs" /> <Compile Include="Assets.cs" />
<Compile Include="Attributes\ReadOnlyAttribute.cs" /> <Compile Include="Attributes\ReadOnlyAttribute.cs" />
<Compile Include="Collections\DetailedGlobalPropSettings.cs" />
<Compile Include="Collections\DunGenExtenderEvents.cs" /> <Compile Include="Collections\DunGenExtenderEvents.cs" />
<Compile Include="Collections\DunGenExtenderPropertiesCollection.cs" /> <Compile Include="Collections\DunGenExtenderPropertiesCollection.cs" />
<Compile Include="Collections\ExtenderEvent.cs" /> <Compile Include="Collections\ExtenderEvent.cs" />

View File

@ -97,6 +97,8 @@ namespace DunGenPlus.Generation {
if (tileMainPath.TryGetValue(tile, out var value)){ if (tileMainPath.TryGetValue(tile, out var value)){
return value; return value;
} }
Plugin.logger.LogWarning("Error with GetMainPathIndexFromTile.\nPLEASE REPORT TO MR. DEV WITH YOUR MOD PACK. THIS SHOULD NOT BE HAPPENING!");
return 0; return 0;
} }

View File

@ -16,6 +16,7 @@ namespace DunGenPlus.Generation {
// Copied and pasted from DunGen // Copied and pasted from DunGen
public static void ProcessGlobalPropsPerMainPath(DungeonGenerator dungeonGenerator){ public static void ProcessGlobalPropsPerMainPath(DungeonGenerator dungeonGenerator){
var localIDs = Properties.MainPathProperties.MainPathDetails.SelectMany(d => d.LocalGroupProps).Select(x => x.ID).ToHashSet(); var localIDs = Properties.MainPathProperties.MainPathDetails.SelectMany(d => d.LocalGroupProps).Select(x => x.ID).ToHashSet();
var detailedSettings = Properties.MainPathProperties.DetailedGlobalPropSettings;
// first parameter int is the GlobalProp ID // first parameter int is the GlobalProp ID
// second parameter is the List of GameObjectChanceTable indexed by the main path index // second parameter is the List of GameObjectChanceTable indexed by the main path index
@ -69,26 +70,46 @@ namespace DunGenPlus.Generation {
var list = new List<int>(localDictionary.Count + globalDictionary.Count); var list = new List<int>(localDictionary.Count + globalDictionary.Count);
int ProcessGlobalPropID(GameObjectChanceTable table, int localMax, int globalCount, int globalMax){ void RemoveElementsTooClose(GameObjectChanceTable table, Vector3 position, float minDistance){
if (minDistance <= 0f) return;
var elementsToRemove = new List<GameObjectChance>();
foreach(var item in table.Weights){
if (Vector3.SqrMagnitude(position - item.Value.transform.position) < minDistance * minDistance) {
elementsToRemove.Add(item);
}
}
foreach(var e in elementsToRemove){
table.Weights.Remove(e);
}
}
int ProcessGlobalPropID(GameObjectChanceTable table, int localMax, int globalCount, int globalMax, float minDistance){
localMax = Mathf.Clamp(localMax, 0, table.Weights.Count); localMax = Mathf.Clamp(localMax, 0, table.Weights.Count);
var i = 0; var i = 0;
while(i < localMax && i + globalCount < globalMax){ while(i < localMax && i + globalCount < globalMax){
var random = table.GetRandom(dungeonGenerator.RandomStream, true, 0f, null, true, true); var random = table.GetRandom(dungeonGenerator.RandomStream, true, 0f, null, true, true);
if (random != null && random.Value != null) { if (random != null && random.Value != null) {
random.Value.SetActive(true); random.Value.SetActive(true);
RemoveElementsTooClose(table, random.Value.transform.position, minDistance);
} }
++i; ++i;
} }
return i; return i;
} }
int ProcessRemainingGlobalPropID(GameObjectChanceTable[] tables, int count){ int ProcessRemainingGlobalPropID(GameObjectChanceTable[] tables, int count, float minDistance){
count = Mathf.Clamp(count, 0, tables.Sum(t => t.Weights.Count)); count = Mathf.Clamp(count, 0, tables.Sum(t => t.Weights.Count));
var i = 0; var i = 0;
while(i < count){ while(i < count){
var random = GameObjectChanceTable.GetCombinedRandom(dungeonGenerator.RandomStream, true, 0f, tables); var random = GameObjectChanceTable.GetCombinedRandom(dungeonGenerator.RandomStream, true, 0f, tables);
if (random != null) { if (random != null) {
random.SetActive(true); random.SetActive(true);
foreach(var t in tables){
RemoveElementsTooClose(t, random.transform.position, minDistance);
}
} }
++i; ++i;
} }
@ -107,12 +128,17 @@ namespace DunGenPlus.Generation {
.Where(x => x.ID == pair.Key) .Where(x => x.ID == pair.Key)
.FirstOrDefault(); .FirstOrDefault();
var detailedPropSettings = detailedSettings
.Where(x => x.ID == pair.Key)
.FirstOrDefault();
var minDistance = detailedPropSettings != null ? detailedPropSettings.MinimumDistance : -1f;
if (globalPropSettings != null){ if (globalPropSettings != null){
var tableClone = pair.Value.Clone(); var tableClone = pair.Value.Clone();
var globalMax = globalPropSettings.Count.GetRandom(dungeonGenerator.RandomStream); var globalMax = globalPropSettings.Count.GetRandom(dungeonGenerator.RandomStream);
var spawned = ProcessGlobalPropID(tableClone, globalMax, 0, globalMax); var spawned = ProcessGlobalPropID(tableClone, globalMax, 0, globalMax, minDistance);
Plugin.logger.LogDebug($"Global ID: {pair.Key} ({spawned} / {globalMax})"); Plugin.logger.LogDebug($"Global ID: {pair.Key} ({spawned} / {globalMax}). Min Dist: {minDistance}");
list.Add(pair.Key); list.Add(pair.Key);
} }
} }
@ -132,15 +158,22 @@ namespace DunGenPlus.Generation {
.Where(x => x.ID == globalPropId) .Where(x => x.ID == globalPropId)
.FirstOrDefault(); .FirstOrDefault();
var detailedPropSettings = detailedSettings
.Where(x => x.ID == pair.Key)
.FirstOrDefault();
var minDistance = detailedPropSettings != null ? detailedPropSettings.MinimumDistance : -1f;
if (globalPropSettings != null){ if (globalPropSettings != null){
var globalCount = 0; var globalCount = 0;
var globalMax = globalPropSettings.Count.GetRandom(dungeonGenerator.RandomStream); var globalMax = globalPropSettings.Count.GetRandom(dungeonGenerator.RandomStream);
var pathDictionary = pair.Value; var pathDictionary = pair.Value;
Plugin.logger.LogDebug($"Local ID: {pair.Key} (Max {globalMax})"); Plugin.logger.LogDebug($"Local ID: {pair.Key} (Max {globalMax}). Min Dist: {minDistance}");
var toRemoveKeys = new List<int>(); var toRemoveKeys = new List<int>();
foreach(var pathPair in pathDictionary){ foreach(var pathPair in pathDictionary){
// try and get local main path properites based on key of Dictionary<MainPathIndex (int), GlobalProps (GameObjectChanceTable)> // try and get local main path properites based on key of Dictionary<MainPathIndex (int), GlobalProps (GameObjectChanceTable)>
var mainPathIndex = pathPair.Key; var mainPathIndex = pathPair.Key;
var localGroupProps = Properties.MainPathProperties.GetMainPathDetails(mainPathIndex).LocalGroupProps; var localGroupProps = Properties.MainPathProperties.GetMainPathDetails(mainPathIndex).LocalGroupProps;
@ -156,7 +189,7 @@ namespace DunGenPlus.Generation {
var tableClone = pathPair.Value.Clone(); var tableClone = pathPair.Value.Clone();
var localMax = localPropSettings.Count.GetRandom(dungeonGenerator.RandomStream); var localMax = localPropSettings.Count.GetRandom(dungeonGenerator.RandomStream);
var spawned = ProcessGlobalPropID(tableClone, localMax, globalCount, globalMax); var spawned = ProcessGlobalPropID(tableClone, localMax, globalCount, globalMax, minDistance);
globalCount += spawned; globalCount += spawned;
Plugin.logger.LogDebug($"Main Path {mainPathIndex}: Local ({spawned} / {localMax}), Global ({globalCount} / {globalMax})"); Plugin.logger.LogDebug($"Main Path {mainPathIndex}: Local ({spawned} / {localMax}), Global ({globalCount} / {globalMax})");
@ -176,7 +209,7 @@ namespace DunGenPlus.Generation {
Plugin.logger.LogDebug($"Combining main paths ({combine}) into one GameObjectChanceTable"); Plugin.logger.LogDebug($"Combining main paths ({combine}) into one GameObjectChanceTable");
var combinedTable = pathDictionary.Select(d => d.Value).ToArray(); var combinedTable = pathDictionary.Select(d => d.Value).ToArray();
var spawned = ProcessRemainingGlobalPropID(combinedTable, globalMax - globalCount); var spawned = ProcessRemainingGlobalPropID(combinedTable, globalMax - globalCount, minDistance);
globalCount += spawned; globalCount += spawned;
Plugin.logger.LogDebug($"Spawned remaining props ({globalCount} / {globalMax})"); Plugin.logger.LogDebug($"Spawned remaining props ({globalCount} / {globalMax})");
} }
@ -187,45 +220,6 @@ namespace DunGenPlus.Generation {
} }
} }
/*
Plugin.logger.LogError("Spawned");
var colors = new Color[] { Color.red, Color.blue };
foreach(var tile in dungeonGenerator.CurrentDungeon.AllTiles){
var mainPathIndex = GetMainPathIndexFromTile(tile);
foreach(var globalProp in tile.GetComponentsInChildren<GlobalProp>()){
if (globalProp.PropGroupID == 1717){
//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];
// newGameObject.SetActive(true);
}
if (globalProp.PropGroupID == 1718){
//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];
//newGameObject.SetActive(true);
}
}
}
*/
} }
} }

View File

@ -394,10 +394,14 @@ namespace DunGenPlus.Patches {
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(DungeonGenerator), "ProcessGlobalProps")] [HarmonyPatch(typeof(DungeonGenerator), "ProcessGlobalProps")]
public static bool ProcessGlobalPropsPatch(ref DungeonGenerator __instance){ public static bool ProcessGlobalPropsPatch(ref DungeonGenerator __instance){
if (DunGenPlusGenerator.Active && DunGenPlusGenerator.Properties.MainPathProperties.MainPathDetails.Any(d => d.LocalGroupProps.Count > 0)){ if (DunGenPlusGenerator.Active){
Plugin.logger.LogDebug("Performing Local Global Props algorithm"); var anyGlobalSettings = DunGenPlusGenerator.Properties.MainPathProperties.DetailedGlobalPropSettings.Count > 0;
DunGenPlusGenerator.ProcessGlobalPropsPerMainPath(__instance); var anyLocalSettings = DunGenPlusGenerator.Properties.MainPathProperties.MainPathDetails.Any(d => d.LocalGroupProps.Count > 0);
return false; if (anyGlobalSettings || anyLocalSettings){
Plugin.logger.LogDebug("Performing Local Global Props algorithm");
DunGenPlusGenerator.ProcessGlobalPropsPerMainPath(__instance);
return false;
}
} }
return true; return true;
} }

View File

@ -19,13 +19,13 @@ using UnityEngine.Assertions;
namespace DunGenPlus { namespace DunGenPlus {
[BepInPlugin(modGUID, modName, modVersion)] [BepInPlugin(modGUID, modName, modVersion)]
[BepInDependency("imabatby.lethallevelloader", "1.2.0.3")] [BepInDependency("imabatby.lethallevelloader", "1.4.0")]
[BepInProcess("Lethal Company.exe")] [BepInProcess("Lethal Company.exe")]
public class Plugin : BaseUnityPlugin { public class Plugin : BaseUnityPlugin {
internal const string modGUID = "dev.ladyalice.dungenplus"; internal const string modGUID = "dev.ladyalice.dungenplus";
private const string modName = "Dungeon Generation Plus"; private const string modName = "Dungeon Generation Plus";
private const string modVersion = "1.3.3"; private const string modVersion = "1.3.4";
internal readonly Harmony Harmony = new Harmony(modGUID); internal readonly Harmony Harmony = new Harmony(modGUID);