Implemented TileExtender

Fixed error spam with DoorwaySisters
Added GenericScriptingParent
Added failsafe to GlobalProp algorithm
Removed DoorwayCleanup
This commit is contained in:
LadyAliceMargatroid 2025-02-10 12:36:57 -08:00
parent 11ddc7dea0
commit 8e30a1b6e6
25 changed files with 267 additions and 368 deletions

View File

@ -12,16 +12,20 @@ namespace DunGenPlus.Collections {
public class DetailedGlobalPropSettings {
internal const string MinimumDistanceTooltip = "The minimum distance between each Global Props of this id.";
internal const string GlobalCountMustBeReachedTooltip = "If true, when the global limit is not reached due to minimum distance, ignore it and try to reach the global limit anyway.";
public int ID;
[Space]
[Tooltip(MinimumDistanceTooltip)]
public float MinimumDistance;
public bool GlobalCountMustBeReached = true;
public DetailedGlobalPropSettings(int id, float minimumDistance) {
public DetailedGlobalPropSettings(int id, float minimumDistance, bool globalCountMustBeReached)
{
ID = id;
MinimumDistance = minimumDistance;
GlobalCountMustBeReached = globalCountMustBeReached;
}
}
}

View File

@ -11,7 +11,6 @@ namespace DunGenPlus.Collections {
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 MinimumDistanceBetweenPropsTooltip = "If true, Global Props of this id MUST have a minimum distance between each other.";
public int ID;

View File

@ -13,35 +13,51 @@ namespace DunGenPlus.Collections {
public TileExtender PrefabTileExtender { get; internal set; }
public List<DoorwayProxy> Entrances { get; internal set; }
public List<DoorwayProxy> Exits { get; internal set; }
public List<DoorwayProxy> OverlappingDoorways { get; internal set; }
public bool EntranceExitInterchangable { get; internal set; }
public TileExtenderProxy(TileExtenderProxy existingTileExtenderProxy) {
TileProxy = existingTileExtenderProxy.TileProxy;
public TileExtenderProxy(TileProxy tileProxy, TileExtenderProxy existingTileExtenderProxy) {
TileProxy = tileProxy;
PrefabTileExtender = existingTileExtenderProxy.PrefabTileExtender;
Entrances = new List<DoorwayProxy>();
Exits = new List<DoorwayProxy>();
OverlappingDoorways = new List<DoorwayProxy>();
foreach(var existingDoorway in TileProxy.doorways) {
if (existingTileExtenderProxy.Entrances.Contains(existingDoorway)) Entrances.Add(existingDoorway);
if (existingTileExtenderProxy.Exits.Contains(existingDoorway)) Exits.Add(existingDoorway);
EntranceExitInterchangable = existingTileExtenderProxy.EntranceExitInterchangable;
var existingTile = existingTileExtenderProxy.TileProxy;
for(var i = 0; i < tileProxy.doorways.Count; ++i){
var doorway = tileProxy.doorways[i];
var existingDoorway = existingTile.doorways[i];
if (existingTileExtenderProxy.Entrances.Contains(existingDoorway)) Entrances.Add(doorway);
if (existingTileExtenderProxy.Exits.Contains(existingDoorway)) Exits.Add(doorway);
if (existingTileExtenderProxy.OverlappingDoorways.Contains(existingDoorway)) OverlappingDoorways.Add(doorway);
}
}
public TileExtenderProxy(TileProxy tileProxy) {
TileProxy = tileProxy;
PrefabTileExtender = tileProxy.Prefab.GetComponent<TileExtender>();
Entrances = new List<DoorwayProxy>();
Exits = new List<DoorwayProxy>();
OverlappingDoorways = new List<DoorwayProxy>();
if (PrefabTileExtender == null) {
if (tileProxy.Entrance != null) Entrances.Add(tileProxy.Entrance);
if (tileProxy.Exit != null) Exits.Add(tileProxy.Exit);
EntranceExitInterchangable = false;
return;
}
foreach(var proxyDoorway in tileProxy.doorways) {
if (PrefabTileExtender.entrances.Contains(proxyDoorway.DoorwayComponent)) Entrances.Add(proxyDoorway);
if (PrefabTileExtender.exits.Contains(proxyDoorway.DoorwayComponent)) Exits.Add(proxyDoorway);
if (PrefabTileExtender.overlappingDoorways.Contains(proxyDoorway.DoorwayComponent)) OverlappingDoorways.Add(proxyDoorway);
}
EntranceExitInterchangable = PrefabTileExtender.entranctExitInterchangable;
}
}
}

View File

@ -1,73 +0,0 @@
using DunGen;
using DunGenPlus.Components.DoorwayCleanupScripting;
using DunGenPlus.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components {
public class DoorwayCleanup : MonoBehaviour, IDungeonCompleteReceiver {
[Header("OBSOLUTE. Please use DoorwayScriptingParent")]
[Header("Doorway References")]
[Tooltip("The doorway reference.")]
public Doorway doorway;
[Tooltip("The connectors scene objects of the doorway.\n\nHighly advise to empty the corresponding list in the doorway.")]
public List<GameObject> connectors;
[Tooltip("The blockers scene objects of the doorway.\n\nHighly advise to empty the corresponding list in the doorway.")]
public List<GameObject> blockers;
[Tooltip("The doorway gameobject target for the DoorwayCleanupScripts. Can be null.")]
public GameObject doorwayGameObject;
[Header("Overrides")]
[Tooltip("Mainly for code purposes. Forces the connectors to be active.")]
public bool overrideConnector;
[Tooltip("Mainly for code purposes. Forces the blockers to be active.")]
public bool overrideBlocker;
[Tooltip("Mainly for code purposes. Forces the doorway gameobject to be disabled.")]
public bool overrideNoDoorway;
public void OnDungeonComplete(Dungeon dungeon) {
SetBlockers(true);
DoorwayManager.AddDoorwayCleanup(this);
}
public void Cleanup(){
// start up like in original
SwitchConnectorBlocker(doorway.ConnectedDoorway != null);
var cleanupList = GetComponentsInChildren<DoorwayCleanupScript>();
foreach(var c in cleanupList) c.Cleanup(this);
if (overrideNoDoorway) SwitchDoorwayGameObject(false);
// clean up like in original
foreach(var c in connectors){
if (!c.activeSelf) UnityEngine.Object.DestroyImmediate(c, false);
}
foreach(var b in blockers){
if (!b.activeSelf) UnityEngine.Object.DestroyImmediate(b, false);
}
}
public void SetBlockers(bool state){
foreach(var b in blockers) b.SetActive(state);
}
public void SwitchConnectorBlocker(bool isConnector){
if (overrideConnector) isConnector = true;
if (overrideBlocker) isConnector = false;
foreach(var c in connectors) c.SetActive(isConnector);
foreach(var b in blockers) b.SetActive(!isConnector);
}
public void SwitchDoorwayGameObject(bool isActive){
doorwayGameObject?.SetActive(isActive);
}
}
}

View File

@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components.DoorwayCleanupScripting {
[Obsolete("Please use DoorwayScriptingParent")]
public class DCSConnectorBlockerSpawnedPrefab : DoorwayCleanupScript {
public enum Action { SwitchToConnector, SwitchToBlocker };
[Header("Calls switch action\nif Doorway instantiates a Connector/Blocker prefab with the target's name")]
[Header("Switch Action")]
public Action switchAction;
[Header("Target")]
public GameObject target;
public override void Cleanup(DoorwayCleanup parent) {
var result = false;
foreach(Transform t in parent.doorway.transform){
if (t.gameObject.activeSelf && t.name.Contains(target.name)) {
result = true;
break;
}
}
if (result) {
parent.SwitchConnectorBlocker(switchAction == Action.SwitchToConnector);
}
}
}
}

View File

@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components.DoorwayCleanupScripting {
[Obsolete("Please use DoorwayScriptingParent")]
public class DCSRemoveDoorwayConnectedDoorway : DoorwayCleanupScriptDoorwayCompare {
[Header("Removes Doorway Gameobject\nif the neighboring doorway's priority matches the operation comparison")]
[Header("Operation Comparison")]
public int doorwayPriority;
public int doorwayPriorityB;
public Operation operation = Operation.Equal;
public override void Cleanup(DoorwayCleanup parent) {
var doorway = parent.doorway;
if (doorway.connectedDoorway == null) return;
var result = GetOperation(operation).Invoke(doorway.connectedDoorway, new Arguments(doorwayPriority, doorwayPriorityB));
if (result) {
parent.SwitchDoorwayGameObject(false);
}
}
}
}

View File

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components.DoorwayCleanupScripting {
[Obsolete("Please use DoorwayScriptingParent")]
public class DCSRemoveDoorwaySpawnedPrefab : DoorwayCleanupScript {
[Header("Removes Doorway Gameobject\nif Doorway instantiates a Connector/Blocker prefab with the target's name")]
[Header("Target")]
public GameObject target;
public override void Cleanup(DoorwayCleanup parent) {
var result = false;
foreach(Transform t in parent.doorway.transform){
if (t.gameObject.activeSelf && t.name.Contains(target.name)) {
result = true;
break;
}
}
if (result) {
parent.SwitchDoorwayGameObject(false);
}
}
}
}

View File

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components.DoorwayCleanupScripting {
[Obsolete("Please use DoorwayScriptingParent")]
public class DCSRemoveGameObjectsConnectedDoorway : DoorwayCleanupScriptDoorwayCompare {
[Header("Removes target GameObjects\nif the neighboring doorway's priority matches the operation comparison")]
[Header("Operation Comparison")]
public int doorwayPriority;
public int doorwayPriorityB;
public Operation operation = Operation.Equal;
[Header("Targets")]
public List<GameObject> targets;
public override void Cleanup(DoorwayCleanup parent) {
var doorway = parent.doorway;
if (doorway.connectedDoorway == null) return;
var result = GetOperation(operation).Invoke(doorway.connectedDoorway, new Arguments(doorwayPriority, doorwayPriorityB));
if (result) {
foreach(var t in targets) t.SetActive(false);
}
}
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components.DoorwayCleanupScripting {
[Obsolete("Please use DoorwayScriptingParent")]
public abstract class DoorwayCleanupScript : MonoBehaviour {
public abstract void Cleanup(DoorwayCleanup parent);
}
}

View File

@ -1,99 +0,0 @@
using DunGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components.DoorwayCleanupScripting {
[Obsolete("Please use DoorwayScriptingParent")]
public abstract class DoorwayCleanupScriptDoorwayCompare : DoorwayCleanupScript {
public enum Operation {
[InspectorName("Equal (target == value)")]
Equal,
[InspectorName("NotEqual (target != value)")]
NotEqual,
[InspectorName("LessThan (target < value)")]
LessThan,
[InspectorName("GreaterThan (target > value)")]
GreaterThan,
[InspectorName("LessThanEq (target <= value)")]
LessThanEq,
[InspectorName("GreaterThanEw (target >= value)")]
GreaterThanEq,
[InspectorName("Between (value < target < valueB)")]
Between,
[InspectorName("BetweenEq (value <= target <= valueB)")]
BetweenEq
}
public struct Arguments{
public int parameterA;
public int parameterB;
public Arguments(int parameterA, int parameterB){
this.parameterA = parameterA;
this.parameterB = parameterB;
}
public static explicit operator Arguments((int a, int b) pair) => new Arguments(pair.a, pair.b);
}
public Func<Doorway, Arguments, bool> GetOperation(Operation operation){
switch(operation){
case Operation.Equal:
return EqualOperation;
case Operation.NotEqual:
return NotEqualOperation;
case Operation.LessThan:
return LessThanOperation;
case Operation.GreaterThan:
return GreaterThanOperation;
case Operation.LessThanEq:
return LessThanEqualOperation;
case Operation.GreaterThanEq:
return GreaterThanEqualOperation;
case Operation.Between:
return BetweenOperation;
case Operation.BetweenEq:
return BetweenEqualOperation;
}
return null;
}
public bool EqualOperation(Doorway other, Arguments arguments){
return other.DoorPrefabPriority == arguments.parameterA;
}
public bool NotEqualOperation(Doorway other, Arguments arguments){
return other.DoorPrefabPriority != arguments.parameterA;
}
public bool LessThanOperation(Doorway other, Arguments arguments){
return other.DoorPrefabPriority < arguments.parameterA;
}
public bool GreaterThanOperation(Doorway other, Arguments arguments){
return other.DoorPrefabPriority > arguments.parameterA;
}
public bool LessThanEqualOperation(Doorway other, Arguments arguments){
return other.DoorPrefabPriority <= arguments.parameterA;
}
public bool GreaterThanEqualOperation(Doorway other, Arguments arguments){
return other.DoorPrefabPriority >= arguments.parameterA;
}
public bool BetweenOperation(Doorway other, Arguments arguments){
return arguments.parameterA < other.DoorPrefabPriority && other.DoorPrefabPriority < arguments.parameterB;
}
public bool BetweenEqualOperation(Doorway other, Arguments arguments){
return arguments.parameterA <= other.DoorPrefabPriority && other.DoorPrefabPriority <= arguments.parameterB;
}
}
}

View File

@ -20,7 +20,7 @@ namespace DunGenPlus.Components {
}
[Tooltip("The list of 'sister' doorways.\n\nUseDoorwaySisters must be toggled in DunGenExtender for this component to be used.\n\nThis doorway will not generate if it's an intersecting doorway, any of it's 'sister' doorways are generated, and both this doorway and the 'sister' doorway lead to the same tile.")]
public List<Doorway> sisters;
public List<Doorway> sisters = new List<Doorway>();
void OnValidate(){
var sis = sisters.Select(s => s.GetComponent<DoorwaySisters>());

View File

@ -55,9 +55,8 @@ namespace DunGenPlus.Components.Scripting {
}
public override EvaluationContext CreateContext() {
var context = new EvaluationContext(GetFields);
var context = base.CreateContext();
context.RegisterFunction("doorwaySpawnedGameObject", new FunctionRoutine(2, doorwaySpawnedGameObjectFunction));
return context;
}
@ -73,7 +72,7 @@ namespace DunGenPlus.Components.Scripting {
return ExpressionToken.False;
}
(object, ValueTypeHint) GetFields(string field) {
public override (object, ValueTypeHint) GetFields(string field) {
var split = field.Split('.');
if (split.Length <= 1) {

View File

@ -124,6 +124,13 @@ namespace DunGenPlus.Components.Scripting {
}
}
public NamedGameObjectReference GetNamedGameObject(string name){
if (namedDictionary.TryGetValue(name, out var obj)){
return obj;
}
return null;
}
public void SetNamedGameObjectOverrideState(string name, OverrideGameObjectState state){
if (namedDictionary.TryGetValue(name, out var obj)){
obj.overrideState = state;
@ -138,15 +145,34 @@ namespace DunGenPlus.Components.Scripting {
}
}
protected bool CheckIfNotNull(object target, string name){
if (target == null) {
Utils.Utility.PrintLog($"{name} was null", BepInEx.Logging.LogLevel.Error);
return false;
}
return true;
public virtual EvaluationContext CreateContext() {
var context = new EvaluationContext(GetFields);
context.RegisterFunction("isAnyActive", new FunctionRoutine(1, isAnyActiveFunction));
context.RegisterFunction("isAllActive", new FunctionRoutine(1, isAllActiveFunction));
return context;
}
public abstract EvaluationContext CreateContext();
public virtual (object, ValueTypeHint) GetFields(string field) {
return default;
}
ExpressionToken isAnyActiveFunction(EvaluationContext context, ExpressionToken[] parameters) {
var targetName = parameters[0].Value;
var target = GetNamedGameObject(targetName);
if (target != null) {
return target.gameObjects.Any(x => x != null && x.activeSelf) ? ExpressionToken.True : ExpressionToken.False;
}
return ExpressionToken.False;
}
ExpressionToken isAllActiveFunction(EvaluationContext context, ExpressionToken[] parameters) {
var targetName = parameters[0].Value;
var target = GetNamedGameObject(targetName);
if (target != null) {
return target.gameObjects.All(x => x != null && x.activeSelf) ? ExpressionToken.True : ExpressionToken.False;
}
return ExpressionToken.False;
}
}
}

View File

@ -0,0 +1,15 @@
using DunGen;
using Soukoku.ExpressionParser;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace DunGenPlus.Components.Scripting
{
internal class GenericScriptingParent : DunGenPlusScriptingParent<Transform> {
}
}

View File

@ -11,8 +11,8 @@ namespace DunGenPlus.Components {
public List<Doorway> entrances = new List<Doorway>();
public List<Doorway> exits = new List<Doorway>();
public bool entranctExitInterchangable;
public List<Doorway> overlappingDoorways = new List<Doorway>();
}
}

View File

@ -149,13 +149,6 @@
<Compile Include="Collections\NodeArchetype.cs" />
<Compile Include="Collections\NullObject.cs" />
<Compile Include="Collections\TileExtenderProxy.cs" />
<Compile Include="Components\DoorwayCleanup.cs" />
<Compile Include="Components\DoorwayCleanupScripting\DCSRemoveDoorwayConnectedDoorway.cs" />
<Compile Include="Components\DoorwayCleanupScripting\DCSRemoveDoorwaySpawnedPrefab.cs" />
<Compile Include="Components\DoorwayCleanupScripting\DCSRemoveGameObjectsConnectedDoorway.cs" />
<Compile Include="Components\DoorwayCleanupScripting\DoorwayCleanupScript.cs" />
<Compile Include="Components\DoorwayCleanupScripting\DCSConnectorBlockerSpawnedPrefab.cs" />
<Compile Include="Components\DoorwayCleanupScripting\DoorwayCleanupScriptDoorwayCompare.cs" />
<Compile Include="Components\DoorwaySisters.cs" />
<Compile Include="Components\MainRoomDoorwayGroups.cs" />
<Compile Include="Components\Props\SpawnSyncedObjectCycle.cs" />
@ -163,6 +156,7 @@
<Compile Include="Components\Scripting\DoorwayScriptingParent.cs" />
<Compile Include="Components\Scripting\DunGenPlusScript.cs" />
<Compile Include="Components\Scripting\DunGenPlusScriptingParent.cs" />
<Compile Include="Components\Scripting\GenericScriptingParent.cs" />
<Compile Include="DevTools\Panels\AssetsPanel.cs" />
<Compile Include="ExpressionParser\EvaluationContext.cs" />
<Compile Include="ExpressionParser\Evaluator.cs" />
@ -185,6 +179,7 @@
<Compile Include="Managers\EnemyManager.cs" />
<Compile Include="Managers\ScrapItemManager.cs" />
<Compile Include="Patches\BranchCountHelperPatch.cs" />
<Compile Include="Patches\DoorwayPairFinderPatch.cs" />
<Compile Include="Patches\DungeonPatch.cs" />
<Compile Include="Patches\TileProxyPatch.cs" />
<Compile Include="PluginConfig.cs" />

View File

@ -14,6 +14,7 @@ namespace DunGenPlus.Generation {
internal partial class DunGenPlusGenerator {
// Copied and pasted from DunGen
// And heavily franksteined
public static void ProcessGlobalPropsPerMainPath(DungeonGenerator dungeonGenerator){
var localIDs = Properties.MainPathProperties.MainPathDetails.SelectMany(d => d.LocalGroupProps).Select(x => x.ID).ToHashSet();
var detailedSettings = Properties.MainPathProperties.DetailedGlobalPropSettings;
@ -23,6 +24,7 @@ namespace DunGenPlus.Generation {
var localDictionary = new Dictionary<int, Dictionary<int, GameObjectChanceTable>>();
// default dictionary
// first parmeter int is the Global Prop ID
var globalDictionary = new Dictionary<int, GameObjectChanceTable>();
foreach(var tile in dungeonGenerator.CurrentDungeon.AllTiles){
@ -54,6 +56,7 @@ namespace DunGenPlus.Generation {
}
}
// deactive in preparation
foreach(var dictionary in localDictionary.Values){
foreach(var table2 in dictionary.Values){
foreach(var gameObjectChance in table2.Weights){
@ -70,7 +73,8 @@ namespace DunGenPlus.Generation {
var list = new List<int>(localDictionary.Count + globalDictionary.Count);
void RemoveElementsTooClose(GameObjectChanceTable table, Vector3 position, float minDistance){
// min distance behaviour
void RemoveElementsTooClose(GameObjectChanceTable table, GameObjectChanceTable reserveTable, Vector3 position, float minDistance){
if (minDistance <= 0f) return;
var elementsToRemove = new List<GameObjectChance>();
@ -82,33 +86,40 @@ namespace DunGenPlus.Generation {
foreach(var e in elementsToRemove){
table.Weights.Remove(e);
reserveTable.Weights.Add(e);
}
}
int ProcessGlobalPropID(GameObjectChanceTable table, int localMax, int globalCount, int globalMax, float minDistance){
// normal default behaviour
int ProcessGlobalPropID(GameObjectChanceTable table, GameObjectChanceTable reserveTable, int localMax, int globalCount, int globalMax, float minDistance){
localMax = Mathf.Clamp(localMax, 0, table.Weights.Count);
var i = 0;
while(i < localMax && i + globalCount < globalMax){
if (table.Weights.Count == 0) return i;
var random = table.GetRandom(dungeonGenerator.RandomStream, true, 0f, null, true, true);
if (random != null && random.Value != null) {
random.Value.SetActive(true);
RemoveElementsTooClose(table, random.Value.transform.position, minDistance);
RemoveElementsTooClose(table, reserveTable, random.Value.transform.position, minDistance);
}
++i;
}
return i;
}
int ProcessRemainingGlobalPropID(GameObjectChanceTable[] tables, int count, float minDistance){
// UseToReachGlobalPropLimit behaviour
int ProcessRemainingGlobalPropID(GameObjectChanceTable[] tables, GameObjectChanceTable reserveTable, int count, float minDistance){
count = Mathf.Clamp(count, 0, tables.Sum(t => t.Weights.Count));
var i = 0;
while(i < count){
if (tables.Sum(t => t.Weights.Count) == 0) return i;
var random = GameObjectChanceTable.GetCombinedRandom(dungeonGenerator.RandomStream, true, 0f, tables);
if (random != null) {
random.SetActive(true);
foreach(var t in tables){
RemoveElementsTooClose(t, random.transform.position, minDistance);
RemoveElementsTooClose(t, reserveTable, random.transform.position, minDistance);
}
}
++i;
@ -116,13 +127,29 @@ namespace DunGenPlus.Generation {
return i;
}
// cause min distance can mean that global prop limit is not reached
// fuck it, make it happen
int ProcessRemainingGlobalPropIDNoMatterWhat(GameObjectChanceTable table, int count){
count = Mathf.Clamp(count, 0, table.Weights.Count);
var i = 0;
while(i < count){
if (table.Weights.Count == 0) return i;
var random = table.GetRandom(dungeonGenerator.RandomStream, true, 0f, null, true, true);
if (random != null) {
random.Value.SetActive(true);
}
++i;
}
return i;
}
using(var enumerator = globalDictionary.GetEnumerator()){
while(enumerator.MoveNext()){
var pair = enumerator.Current;
if (list.Contains(pair.Key)){
Plugin.logger.LogWarning("Dungeon Flow contains multiple entries for the global prop group ID: " + pair.Key.ToString() + ". Only the first entry will be used.");
} else {
//Plugin.logger.LogWarning($"{pair.Key}: Global");
var globalPropSettings = dungeonGenerator.DungeonFlow.GlobalProps
.Where(x => x.ID == pair.Key)
@ -131,15 +158,25 @@ namespace DunGenPlus.Generation {
var detailedPropSettings = detailedSettings
.Where(x => x.ID == pair.Key)
.FirstOrDefault();
var reserveTable = new GameObjectChanceTable();
var minDistance = detailedPropSettings != null ? detailedPropSettings.MinimumDistance : -1f;
var reachGlobalLimitNoMatterWhat = detailedPropSettings != null ? detailedPropSettings.GlobalCountMustBeReached : true;
if (globalPropSettings != null){
var tableClone = pair.Value.Clone();
var table = pair.Value;
var globalMax = globalPropSettings.Count.GetRandom(dungeonGenerator.RandomStream);
var spawned = ProcessGlobalPropID(tableClone, globalMax, 0, globalMax, minDistance);
var spawned = ProcessGlobalPropID(table, reserveTable, globalMax, 0, globalMax, minDistance);
Plugin.logger.LogDebug($"Global ID: {pair.Key} ({spawned} / {globalMax}). Min Dist: {minDistance}");
list.Add(pair.Key);
// failsafe
if (reachGlobalLimitNoMatterWhat && spawned < globalMax) {
Plugin.logger.LogDebug($"MinDistance be damned, forcing {globalMax - spawned} more to spawn");
spawned += ProcessRemainingGlobalPropIDNoMatterWhat(reserveTable, globalMax - spawned);
Plugin.logger.LogDebug($"Spawned remaining props ({spawned} / {globalMax})");
}
}
}
}
@ -161,7 +198,11 @@ namespace DunGenPlus.Generation {
var detailedPropSettings = detailedSettings
.Where(x => x.ID == pair.Key)
.FirstOrDefault();
var reserveTable = new GameObjectChanceTable();
var minDistance = detailedPropSettings != null ? detailedPropSettings.MinimumDistance : -1f;
var reachGlobalLimitNoMatterWhat = detailedPropSettings != null ? detailedPropSettings.GlobalCountMustBeReached : true;
reachGlobalLimitNoMatterWhat = true;
if (globalPropSettings != null){
var globalCount = 0;
@ -172,8 +213,6 @@ namespace DunGenPlus.Generation {
var toRemoveKeys = new List<int>();
foreach(var pathPair in pathDictionary){
// try and get local main path properites based on key of Dictionary<MainPathIndex (int), GlobalProps (GameObjectChanceTable)>
var mainPathIndex = pathPair.Key;
var localGroupProps = Properties.MainPathProperties.GetMainPathDetails(mainPathIndex).LocalGroupProps;
@ -186,15 +225,20 @@ namespace DunGenPlus.Generation {
continue;
}
var tableClone = pathPair.Value.Clone();
var tableClone = pathPair.Value; // this was cloned, why?
var localMax = localPropSettings.Count.GetRandom(dungeonGenerator.RandomStream);
var spawned = ProcessGlobalPropID(tableClone, localMax, globalCount, globalMax, minDistance);
var spawned = ProcessGlobalPropID(tableClone, reserveTable, localMax, globalCount, globalMax, minDistance);
globalCount += spawned;
Plugin.logger.LogDebug($"Main Path {mainPathIndex}: Local ({spawned} / {localMax}), Global ({globalCount} / {globalMax})");
// all dictionaries that we done using get throw out
if (!localPropSettings.UseToReachGlobalPropLimit) toRemoveKeys.Add(mainPathIndex);
if (!localPropSettings.UseToReachGlobalPropLimit) {
toRemoveKeys.Add(mainPathIndex);
foreach(var w in tableClone.Weights){
reserveTable.Weights.Add(w); // but we still have to include anything remaining back into the reserve
}
}
//Plugin.logger.LogError($"Spawned {spawned} ({globalCount}/{globalMax})");
}
@ -209,11 +253,18 @@ namespace DunGenPlus.Generation {
Plugin.logger.LogDebug($"Combining main paths ({combine}) into one GameObjectChanceTable");
var combinedTable = pathDictionary.Select(d => d.Value).ToArray();
var spawned = ProcessRemainingGlobalPropID(combinedTable, globalMax - globalCount, minDistance);
var spawned = ProcessRemainingGlobalPropID(combinedTable, reserveTable, globalMax - globalCount, minDistance);
globalCount += spawned;
Plugin.logger.LogDebug($"Spawned remaining props ({globalCount} / {globalMax})");
}
// failsafe
if (reachGlobalLimitNoMatterWhat && globalCount < globalMax) {
Plugin.logger.LogDebug($"MinDistance be damned, forcing {globalMax - globalCount} more to spawn");
globalCount += ProcessRemainingGlobalPropIDNoMatterWhat(reserveTable, globalMax - globalCount);
Plugin.logger.LogDebug($"Spawned remaining props ({globalCount} / {globalMax})");
}
list.Add(pair.Key);
}
}

View File

@ -1,6 +1,7 @@
using DunGen;
using DunGen.Graph;
using DunGenPlus.DevTools;
using DunGenPlus.Patches;
using System;
using System.Collections;
using System.Collections.Generic;
@ -95,5 +96,94 @@ namespace DunGenPlus.Generation {
return defaultState || API.IsDevDebugModeActive();
}
public static IEnumerable<DoorwayPair> GetPotentialDoorwayPairsForNonFirstTileAlternate(DoorwayPairFinder __instance){
var previousTile = __instance.PreviousTile;
var previousTileExtended = TileProxyPatch.GetTileExtenderProxy(previousTile);
var tileOrder = __instance.tileOrder;
IEnumerable<DoorwayProxy> validPrevExits;
// default base behaviour
if (!previousTileExtended.EntranceExitInterchangable){
validPrevExits = previousTile.UnusedDoorways.Intersect(previousTileExtended.Exits);
}
// interchangable behaviour
else {
// check if previous tile used a specific exit/entrance
// they used an exit as their entrance, so use entrances
if (previousTile.UsedDoorways.Intersect(previousTileExtended.Exits).Any()){
validPrevExits = previousTile.UnusedDoorways.Intersect(previousTileExtended.Entrances);
}
// they used an entrance as their entrance, so use exits
else if (previousTile.UsedDoorways.Intersect(previousTileExtended.Entrances).Any()){
validPrevExits = previousTile.UnusedDoorways.Intersect(previousTileExtended.Exits);
}
// uhh i guess it's fine?
else {
validPrevExits = new List<DoorwayProxy>();
}
}
var requiresSpecExit = validPrevExits.Any();
foreach(var previousDoor in previousTile.UnusedDoorways) {
// overlapping doors aren't allowed to be potential doorway pairs since this function only finds entrance/exits
// or have fun
var isPrevOverlappingDoor = previousTileExtended.OverlappingDoorways.Contains(previousDoor);
if (isPrevOverlappingDoor) {
continue;
}
// only use allowed doorway exits
if (requiresSpecExit && !validPrevExits.Contains(previousDoor)) continue;
foreach(var tileWeight in __instance.TileWeights) {
// skip if not ever considered
if (!tileOrder.Contains(tileWeight)) continue;
var nextTile = __instance.GetTileTemplateDelegate(tileWeight.Value);
var nextTileExtended = TileProxyPatch.GetTileExtenderProxy(nextTile);
var weight = (float)(tileOrder.Count - tileOrder.IndexOf(tileWeight));
if (__instance.IsTileAllowedPredicate != null && !__instance.IsTileAllowedPredicate(previousTile, nextTile, ref weight)) continue;
foreach(var nextDoor in nextTile.Doorways) {
// check for previous and next
// i forget next which causes problems obviously
var isNextOverlappingDoor = nextTileExtended.OverlappingDoorways.Contains(nextDoor);
if (isNextOverlappingDoor) {
continue;
}
bool AllowEntranceAndExitPair(IEnumerable<DoorwayProxy> entrances, IEnumerable<DoorwayProxy> exits){
// only use allowed doorway entrances
if (entrances.Any() && !entrances.Contains(nextDoor)) return false;
// skip if desiganted as an exit
if (exits.Contains(nextDoor)) return false;
return true;
}
// normal default behaviour
if (!nextTileExtended.EntranceExitInterchangable){
if (!AllowEntranceAndExitPair(nextTileExtended.Entrances, nextTileExtended.Exits)) continue;
}
// interchangable behaviour
else {
var firstCheck = AllowEntranceAndExitPair(nextTileExtended.Entrances, nextTileExtended.Exits);
var secondCheck = AllowEntranceAndExitPair(nextTileExtended.Exits, nextTileExtended.Entrances);
if (!firstCheck && !secondCheck) continue;
}
var doorwayWeight = 0f;
if (__instance.IsValidDoorwayPairing(previousDoor, nextDoor, previousTile, nextTile, ref doorwayWeight))
yield return new DoorwayPair(previousTile, previousDoor, nextTile, nextDoor, tileWeight.TileSet, weight, doorwayWeight);
}
}
}
}
}
}

View File

@ -10,7 +10,6 @@ using DunGenPlus.Components;
using DunGenPlus.Components.Scripting;
using DunGenPlus.Generation;
using DunGenPlus.Utils;
using static DunGenPlus.Managers.DoorwayManager;
namespace DunGenPlus.Managers {
public static class DoorwayManager {
@ -59,10 +58,6 @@ namespace DunGenPlus.Managers {
}
}
public static void AddDoorwayCleanup(DoorwayCleanup cleanup){
//doorwayCleanupList.Add(cleanup);
}
public static void AddDunGenScriptHook(IDunGenScriptingParent script){
scriptingLists[script.GetScriptingHook].Add(script);
}

View File

@ -0,0 +1,27 @@
using DunGen;
using DunGenPlus.Generation;
using HarmonyLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DunGenPlus.Patches {
internal class DoorwayPairFinderPatch {
[HarmonyPostfix]
[HarmonyPatch(typeof(DoorwayPairFinder), "GetPotentialDoorwayPairsForNonFirstTile")]
public static void GenerateBranchPathsPatch(ref DoorwayPairFinder __instance, ref IEnumerable<DoorwayPair> __result){
if (DunGenPlusGenerator.Active) {
__result = DunGenPlusGenerator.GetPotentialDoorwayPairsForNonFirstTileAlternate(__instance);
}
}
}
}

View File

@ -40,7 +40,7 @@ namespace DunGenPlus.Patches {
[HarmonyPostfix]
[HarmonyPatch(typeof(DungeonGenerator), "InnerGenerate")]
public static void InnerGeneratePatch(ref DungeonGenerator __instance, bool isRetry, ref IEnumerator __result){
public static void InnerGeneratePatch(ref DungeonGenerator __instance, bool isRetry){
//Plugin.logger.LogWarning($"InnerGenerate: {DunGenPlusGenerator.Active}, {DunGenPlusGenerator.ActiveAlternative}, {__instance.Status}");
if (API.IsDevDebugModeActive() && !isRetry) {
DevDebugManager.Instance.RecordNewSeed(__instance.ChosenSeed);
@ -55,7 +55,7 @@ namespace DunGenPlus.Patches {
[HarmonyPostfix]
[HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath")]
public static void GenerateMainPathPatch(ref DungeonGenerator __instance, ref IEnumerator __result){
public static void GenerateMainPathPatch(ref DungeonGenerator __instance){
//Plugin.logger.LogWarning($"GenerateMainPath: {DunGenPlusGenerator.Active}, {DunGenPlusGenerator.ActiveAlternative}, {__instance.Status}");
if (DunGenPlusGenerator.Active && DunGenPlusGenerator.ActiveAlternative) {
DunGenPlusGenerator.RandomizeLineArchetypes(__instance, true);
@ -69,7 +69,6 @@ namespace DunGenPlus.Patches {
if (DunGenPlusGenerator.Active && DunGenPlusGenerator.ActiveAlternative) {
__result = DunGenPlusGenerator.GenerateAlternativeMainPaths(__instance);
}
}
[HarmonyTranspiler]

View File

@ -35,7 +35,7 @@ namespace DunGenPlus.Patches {
[HarmonyPatch(typeof(TileProxy), MethodType.Constructor, new Type[] { typeof(TileProxy) })]
[HarmonyPostfix]
public static void TileProxyConstructorExistingPatch(ref TileProxy __instance, TileProxy existingTile){
AddTileExtenderProxy(__instance, new TileExtenderProxy(GetTileExtenderProxy(existingTile)));
AddTileExtenderProxy(__instance, new TileExtenderProxy(__instance, GetTileExtenderProxy(existingTile)));
}
}

View File

@ -27,7 +27,7 @@ namespace DunGenPlus {
internal const string modGUID = "dev.ladyalice.dungenplus";
private const string modName = "Dungeon Generation Plus";
private const string modVersion = "1.4.0";
private const string modVersion = "1.4.1";
internal readonly Harmony Harmony = new Harmony(modGUID);
@ -51,6 +51,7 @@ namespace DunGenPlus {
Harmony.PatchAll(typeof(RoundManagerPatch));
Harmony.PatchAll(typeof(BranchCountHelperPatch));
Harmony.PatchAll(typeof(TileProxyPatch));
Harmony.PatchAll(typeof(DoorwayPairFinderPatch));
try {
Harmony.PatchAll(typeof(LethalLevelLoaderPatches));

Binary file not shown.