Initial commit
This commit is contained in:
parent
1af2b57c1c
commit
eebf00988e
30 changed files with 1883 additions and 21 deletions
25
DunGenPlus/DunGenPlus.sln
Normal file
25
DunGenPlus/DunGenPlus.sln
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.35013.160
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DunGenPlus", "DunGenPlus\DunGenPlus.csproj", "{13CDE60E-1975-463B-9DA1-CCB3F3EBABD8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{13CDE60E-1975-463B-9DA1-CCB3F3EBABD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{13CDE60E-1975-463B-9DA1-CCB3F3EBABD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{13CDE60E-1975-463B-9DA1-CCB3F3EBABD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{13CDE60E-1975-463B-9DA1-CCB3F3EBABD8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F8E7870E-8528-46EE-84B6-8328BFCC093B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
26
DunGenPlus/DunGenPlus/API.cs
Normal file
26
DunGenPlus/DunGenPlus/API.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using DunGen;
|
||||
using DunGen.Graph;
|
||||
|
||||
namespace DunGenPlus
|
||||
{
|
||||
public class API {
|
||||
|
||||
public static bool AddDunGenExtender(DungeonFlow dungeonFlow, DunGenExtender dunGenExtender) {
|
||||
if (Plugin.DunGenExtenders.ContainsKey(dungeonFlow)) {
|
||||
Plugin.logger.LogWarning($"Already contains DunGenExtender asset for {dungeonFlow.name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
Plugin.DunGenExtenders.Add(dungeonFlow, dunGenExtender);
|
||||
Plugin.logger.LogInfo($"Added DunGenExtender asset for {dungeonFlow.name}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
36
DunGenPlus/DunGenPlus/Assets.cs
Normal file
36
DunGenPlus/DunGenPlus/Assets.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using DunGenPlus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BepInEx;
|
||||
using UnityEngine;
|
||||
using LethalLevelLoader;
|
||||
|
||||
namespace DunGenPlus {
|
||||
internal class Assets {
|
||||
|
||||
public static void LoadAssets(){
|
||||
foreach (string text in Directory.GetFiles(Paths.PluginPath, "*.lethalbundle", SearchOption.AllDirectories)) {
|
||||
FileInfo fileInfo = new FileInfo(text);
|
||||
LethalLevelLoader.AssetBundleLoader.AddOnLethalBundleLoadedListener(AutoAddLethalBundle, fileInfo.Name);
|
||||
}
|
||||
}
|
||||
|
||||
static void AutoAddLethalBundle(AssetBundle assetBundle){
|
||||
var extenders = assetBundle.LoadAllAssets<DunGenExtender>();
|
||||
var content = assetBundle.LoadAllAssets<ExtendedContent>();
|
||||
|
||||
if (content.Length == 0) {
|
||||
Plugin.logger.LogWarning($".lethalbundle does not contain any ExtendedContent. Unless you are manually creating and adding your ExtendedDungeonFlow with code, the DunGenExtender will probably not work.");
|
||||
}
|
||||
|
||||
foreach(var e in extenders){
|
||||
API.AddDunGenExtender(e.DungeonFlow, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
15
DunGenPlus/DunGenPlus/Collections/DunGenExtenderEvents.cs
Normal file
15
DunGenPlus/DunGenPlus/Collections/DunGenExtenderEvents.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DunGenPlus.Collections {
|
||||
|
||||
[System.Serializable]
|
||||
public class DunGenExtenderEvents {
|
||||
|
||||
public ExtenderEvent<DunGenExtenderProperties> OnModifyDunGenExtenderProperties = new ExtenderEvent<DunGenExtenderProperties>();
|
||||
|
||||
}
|
||||
}
|
131
DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs
Normal file
131
DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
using DunGen.Graph;
|
||||
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 DunGenExtenderProperties {
|
||||
|
||||
[Header("Main Path")]
|
||||
[Tooltip("The number of main paths.\n\n1 means no additional main paths\n3 means two additional main paths\netc.")]
|
||||
[Range(1, 9)]
|
||||
public int MainPathCount = 1;
|
||||
[Tooltip("The Tile Prefab where the additional main paths will start from. Cannot be null if MainPathCount is more than 1.\n\nHighly advice for this Tile Prefab to have multiple doorways.")]
|
||||
public GameObject MainRoomTilePrefab;
|
||||
|
||||
[Header("Dungeon Bounds")]
|
||||
[Tooltip("If enabled, restricts the dungeon's generation to the bounds described below.\n\nThis will help in condensing the dungeon, but it will increase the chance of dungeon generation failure (potentially guarantees failure if the bounds is too small).")]
|
||||
public bool UseDungeonBounds = false;
|
||||
[Tooltip("The base size of the bounds.")]
|
||||
public Vector3 DungeonSizeBase = new Vector3(120f, 40f, 80f);
|
||||
[Tooltip("The factor that's multiplied with the base size AND the dungeon's size. The resulting value is added to the base size of the bounds.\n\n0 means that the bound size is not influenced by the dungeon's size and is therefore a constant.")]
|
||||
public Vector3 DungeonSizeFactor = new Vector3(1f, 0f, 1f);
|
||||
[Tooltip("The base positional offset of the bounds.")]
|
||||
public Vector3 DungeonPositionOffset;
|
||||
[Tooltip("The pivot of the bounds.")]
|
||||
public Vector3 DungeonPositionPivot = new Vector3(0.5f, 0f, 0.5f);
|
||||
|
||||
[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 = true;
|
||||
public List<NodeArchetype> NormalNodeArchetypes;
|
||||
internal Dictionary<string, NodeArchetype> _normalNodeArchetypesDictioanry;
|
||||
internal NodeArchetype _defaultNodeArchetype;
|
||||
|
||||
[Header("Doorway Sisters")]
|
||||
[Tooltip("If enabled, the DoorwaySisters component will become active.\n\nThe component prevents an intersecting doorway from generating if it's 'sister' doorway already generated and both doorways would lead to the same neighboring tile.\n\nThis is designed for the scenario where, two neighboring doorways would lead to the same tile, one doorway is a locked door and the other is an open doorway. This would defeat the purpose of the locked door, and such as, this feature exists if needed.\n\nThis feature slows down dungeon generation slightly when enabled.")]
|
||||
public bool UseDoorwaySisters = false;
|
||||
|
||||
[Header("Line Randomizer")]
|
||||
[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<DungeonArchetype> LineRandomizerArchetypes;
|
||||
[Tooltip("The tilesets that will be used for replacement.")]
|
||||
public List<TileSet> LineRandomizerTileSets;
|
||||
[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;
|
||||
|
||||
[Header("Max Shadows Request")]
|
||||
[Tooltip("If enabled, updates the MaxShadowsRequest to MaxShadowsRequestAmount when your dungeon loads.\n\nThis is designed for the scenario where your dungeon, for whatever reason, has too many lights nearby and causes the annoying 'Max shadow requests count reached' warning to spam the logs.")]
|
||||
public bool UseMaxShadowsRequestUpdate = false;
|
||||
[Tooltip("The amount of MaxShadowsRequest.\n\n4 is the game's default value. I find 8 to be more than acceptable.")]
|
||||
public int MaxShadowsRequestAmount = 8;
|
||||
|
||||
internal void SetupProperties(DungeonGenerator generator){
|
||||
_normalNodeArchetypesDictioanry = new Dictionary<string, NodeArchetype>();
|
||||
_defaultNodeArchetype = null;
|
||||
|
||||
foreach(var n in NormalNodeArchetypes) {
|
||||
if (_normalNodeArchetypesDictioanry.ContainsKey(n.label)) {
|
||||
Plugin.logger.LogError($"Label {n.label} already exists. Ignoring latest entry.");
|
||||
continue;
|
||||
}
|
||||
_normalNodeArchetypesDictioanry.Add(n.label, n);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(n.label)) {
|
||||
_defaultNodeArchetype = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DungeonArchetype GetRandomArchetype(string label, RandomStream randomStream) {
|
||||
NodeArchetype node;
|
||||
if (!_normalNodeArchetypesDictioanry.TryGetValue(label, out node)) {
|
||||
node = _defaultNodeArchetype;
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
var archetypes = node.archetypes;
|
||||
var count = archetypes.Count;
|
||||
if (count == 0) return null;
|
||||
|
||||
var index = randomStream.Next(0, count);
|
||||
return archetypes[index];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal Bounds GetDungeonBounds(float dungeonScale) {
|
||||
var size = DungeonSizeBase + Vector3.Scale(DungeonSizeBase * (dungeonScale - 1), DungeonSizeFactor);
|
||||
var offset = DungeonPositionOffset + Vector3.Scale(size, DungeonPositionPivot);
|
||||
return new Bounds(offset, size);
|
||||
}
|
||||
|
||||
internal DunGenExtenderProperties Copy() {
|
||||
var copy = new DunGenExtenderProperties();
|
||||
|
||||
copy.MainPathCount = MainPathCount;
|
||||
copy.MainRoomTilePrefab = MainRoomTilePrefab;
|
||||
|
||||
copy.UseDungeonBounds = UseDungeonBounds;
|
||||
copy.DungeonSizeBase = DungeonSizeBase;
|
||||
copy.DungeonSizeFactor = DungeonSizeFactor;
|
||||
copy.DungeonPositionOffset = DungeonPositionOffset;
|
||||
copy.DungeonPositionPivot = DungeonPositionPivot;
|
||||
|
||||
copy.AddArchetypesToNormalNodes = AddArchetypesToNormalNodes;
|
||||
copy.NormalNodeArchetypes = NormalNodeArchetypes;
|
||||
|
||||
copy.UseDoorwaySisters = UseDoorwaySisters;
|
||||
|
||||
copy.UseLineRandomizer = UseLineRandomizer;
|
||||
copy.LineRandomizerTileSets = LineRandomizerTileSets;
|
||||
copy.LineRandomizerArchetypes = LineRandomizerArchetypes;
|
||||
copy.LineRandomizerTakeCount = LineRandomizerTakeCount;
|
||||
|
||||
copy.UseMaxShadowsRequestUpdate = UseMaxShadowsRequestUpdate;
|
||||
copy.MaxShadowsRequestAmount = MaxShadowsRequestAmount;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
26
DunGenPlus/DunGenPlus/Collections/ExtenderEvent.cs
Normal file
26
DunGenPlus/DunGenPlus/Collections/ExtenderEvent.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DunGenPlus.Collections {
|
||||
public class ExtenderEvent<T> {
|
||||
|
||||
internal event ParameterEvent onParameterEvent;
|
||||
|
||||
public void Invoke(T param) {
|
||||
onParameterEvent?.Invoke(param);
|
||||
}
|
||||
|
||||
public void AddListener(ParameterEvent listener) {
|
||||
onParameterEvent += listener;
|
||||
}
|
||||
|
||||
public void RemoveListener(ParameterEvent listener) {
|
||||
onParameterEvent -= listener;
|
||||
}
|
||||
|
||||
public delegate void ParameterEvent(T param);
|
||||
}
|
||||
}
|
19
DunGenPlus/DunGenPlus/Collections/NodeArchetype.cs
Normal file
19
DunGenPlus/DunGenPlus/Collections/NodeArchetype.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
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 NodeArchetype {
|
||||
[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<DungeonArchetype> archetypes;
|
||||
}
|
||||
|
||||
}
|
72
DunGenPlus/DunGenPlus/Components/DoorwayCleanup.cs
Normal file
72
DunGenPlus/DunGenPlus/Components/DoorwayCleanup.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
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("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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
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 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, doorwayPriority);
|
||||
|
||||
if (result) {
|
||||
parent.SwitchDoorwayGameObject(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
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 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, doorwayPriority);
|
||||
if (result) {
|
||||
foreach(var t in targets) t.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
public abstract class DoorwayCleanupScript : MonoBehaviour {
|
||||
|
||||
public abstract void Cleanup(DoorwayCleanup parent);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using DunGen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
public abstract class DoorwayCleanupScriptDoorwayCompare : DoorwayCleanupScript {
|
||||
|
||||
public enum Operation { Equal, NotEqual, LessThan, GreaterThan }
|
||||
|
||||
public Func<Doorway, int, 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;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool EqualOperation(Doorway other, int doorwayPriority){
|
||||
return other.DoorPrefabPriority == doorwayPriority;
|
||||
}
|
||||
|
||||
public bool NotEqualOperation(Doorway other, int doorwayPriority){
|
||||
return other.DoorPrefabPriority != doorwayPriority;
|
||||
}
|
||||
|
||||
public bool LessThanOperation(Doorway other, int doorwayPriority){
|
||||
return other.DoorPrefabPriority < doorwayPriority;
|
||||
}
|
||||
|
||||
public bool GreaterThanOperation(Doorway other, int doorwayPriority){
|
||||
return other.DoorPrefabPriority > doorwayPriority;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
64
DunGenPlus/DunGenPlus/Components/DoorwaySisters.cs
Normal file
64
DunGenPlus/DunGenPlus/Components/DoorwaySisters.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using DunGen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components {
|
||||
public class DoorwaySisters : MonoBehaviour {
|
||||
|
||||
private Doorway _self;
|
||||
public Doorway Self {
|
||||
get {
|
||||
if (_self == null) {
|
||||
_self = GetComponent<Doorway>();
|
||||
}
|
||||
return _self;
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
void OnValidate(){
|
||||
var sis = sisters.Select(s => s.GetComponent<DoorwaySisters>());
|
||||
foreach(var s in sis) {
|
||||
if (s == null) continue;
|
||||
|
||||
s.TryAddSisterDoorway(Self);
|
||||
}
|
||||
}
|
||||
|
||||
public void TryAddSisterDoorway(Doorway doorway){
|
||||
if (sisters.Contains(doorway)) return;
|
||||
sisters.Add(doorway);
|
||||
}
|
||||
|
||||
public void OnDrawGizmosSelected(){
|
||||
var center = transform.position + Vector3.up;
|
||||
if (sisters == null) return;
|
||||
|
||||
foreach(var sis in sisters){
|
||||
var target = sis.transform.position + Vector3.up;
|
||||
var comp = sis.GetComponent<DoorwaySisters>();
|
||||
|
||||
var self = Self;
|
||||
if (self == null) {
|
||||
Gizmos.color = Color.magenta;
|
||||
} else if (comp == null || comp.sisters == null){
|
||||
Gizmos.color = Color.yellow;
|
||||
} else if (!comp.sisters.Contains(self)) {
|
||||
Gizmos.color = Color.red;
|
||||
} else {
|
||||
Gizmos.color = Color.green;
|
||||
}
|
||||
|
||||
Gizmos.DrawLine(center, target);
|
||||
Gizmos.DrawSphere((center + target) * 0.5f, 0.25f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
36
DunGenPlus/DunGenPlus/Components/MainRoomDoorwayGroups.cs
Normal file
36
DunGenPlus/DunGenPlus/Components/MainRoomDoorwayGroups.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using DunGen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components {
|
||||
public class MainRoomDoorwayGroups : MonoBehaviour {
|
||||
|
||||
[System.Serializable]
|
||||
public class DoorwayList {
|
||||
[Tooltip("For organizing purposes. Has no effect.")]
|
||||
public string name;
|
||||
[Tooltip("The group of doorways.")]
|
||||
public List<Doorway> doorways;
|
||||
|
||||
public bool Contains(Doorway target) {
|
||||
return doorways.Contains(target);
|
||||
}
|
||||
}
|
||||
|
||||
[Tooltip("When an additional main path is being generated, it will get the doorway used for the previous main path, find it's corresponding group below, and prevents the dungeon generation from using that group's doorways until the main paths are all generated.\n\nIf you want this feature, this must be attached to the tile that will act as the MainRoomTilePrefab.\n\nThis is designed for the scenario where you would like the main paths to be generated more evenly throughout the MainRoomTilePrefab.")]
|
||||
public List<DoorwayList> doorwayLists;
|
||||
public List<Doorway> doorwayListFirst => doorwayLists.Count > 0 ? doorwayLists[0].doorways : null;
|
||||
|
||||
public List<Doorway> GrabDoorwayGroup(Doorway target){
|
||||
foreach(var a in doorwayLists){
|
||||
if (a.Contains(target)) return a.doorways;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using DunGen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.Props
|
||||
{
|
||||
public class SpawnSyncedObjectCycle : MonoBehaviour, IDungeonCompleteReceiver {
|
||||
|
||||
public static int cycle;
|
||||
public static Dictionary<int, int> cycleDictionary;
|
||||
|
||||
[Tooltip("The SpawnSyncedObject reference.\n\nWhen the dungeon generation finishes, the spawnPrefab of the referenced SpawnSyncedObject will change to one of the Props based on a cycle. The starting value is random.\n\nThis is designed for the scenario where you have multiple very similar networked gameobjects that serve the same purpose, and you just want them all to spawn equally for diversity sake.")]
|
||||
public SpawnSyncedObject Spawn;
|
||||
[Tooltip("The unique id for this script's cycle.\n\nWhen the dungeon generation finishes, a random cycle value is calculated for each Id. Each script will reference their Id's corresponding cycle value to determine their Prop, and advance the cycle value by 1.")]
|
||||
public int Id;
|
||||
[Tooltip("The list of props that would selected based on a cycle.")]
|
||||
public List<GameObject> Props = new List<GameObject>();
|
||||
|
||||
void Reset(){
|
||||
Spawn = GetComponent<SpawnSyncedObject>();
|
||||
}
|
||||
|
||||
public static void UpdateCycle(int value){
|
||||
Plugin.logger.LogInfo($"Updating SpawnSyncedObject start cycle to {value}");
|
||||
cycle = value;
|
||||
cycleDictionary = new Dictionary<int, int>();
|
||||
}
|
||||
|
||||
public int GetCycle(int id){
|
||||
if (!cycleDictionary.TryGetValue(id, out var value)){
|
||||
value = cycle;
|
||||
cycleDictionary.Add(id, value);
|
||||
}
|
||||
|
||||
cycleDictionary[id] = value + 1;
|
||||
Plugin.logger.LogInfo($"Cycle{id}: {value}");
|
||||
return value;
|
||||
}
|
||||
|
||||
public void OnDungeonComplete(Dungeon dungeon) {
|
||||
var index = GetCycle(Id) % Props.Count;
|
||||
var prefab = Props[index];
|
||||
Spawn.spawnPrefab = prefab;
|
||||
}
|
||||
}
|
||||
}
|
22
DunGenPlus/DunGenPlus/DunGenExtender.cs
Normal file
22
DunGenPlus/DunGenPlus/DunGenExtender.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using DunGen;
|
||||
using DunGen.Graph;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using DunGenPlus.Collections;
|
||||
|
||||
namespace DunGenPlus {
|
||||
|
||||
[CreateAssetMenu(fileName = "DunGenExtender", menuName = "DunGenExtender", order = 1)]
|
||||
public class DunGenExtender : ScriptableObject {
|
||||
|
||||
[Tooltip("DunGenExtender will only influence this DungeonFlow")]
|
||||
public DungeonFlow DungeonFlow;
|
||||
public DunGenExtenderProperties Properties;
|
||||
public DunGenExtenderEvents Events;
|
||||
|
||||
}
|
||||
}
|
119
DunGenPlus/DunGenPlus/DunGenPlus.csproj
Normal file
119
DunGenPlus/DunGenPlus/DunGenPlus.csproj
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{13CDE60E-1975-463B-9DA1-CCB3F3EBABD8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DunGenPlus</RootNamespace>
|
||||
<AssemblyName>DunGenPlus</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>embedded</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>..\..\..\Libraries\0Harmony.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass">
|
||||
<HintPath>..\..\..\Libraries\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-publicized">
|
||||
<HintPath>..\..\..\Libraries\Assembly-CSharp-publicized.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="BepInEx">
|
||||
<HintPath>..\..\..\Libraries\BepInEx.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="BepInEx.Harmony">
|
||||
<HintPath>..\..\..\Libraries\BepInEx.Harmony.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="LethalLevelLoader">
|
||||
<HintPath>..\..\..\Libraries\LethalLevelLoader.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Unity.Collections">
|
||||
<HintPath>..\..\..\Libraries\Unity.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.Core.Runtime, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\Libraries\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.HighDefinition.Config.Runtime, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\Libraries\Unity.RenderPipelines.HighDefinition.Config.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.HighDefinition.Runtime, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\Libraries\Unity.RenderPipelines.HighDefinition.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>..\..\..\Libraries\UnityEngine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AssetBundleModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\Libraries\UnityEngine.AssetBundleModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>..\..\..\Libraries\UnityEngine.CoreModule.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="API.cs" />
|
||||
<Compile Include="Assets.cs" />
|
||||
<Compile Include="Collections\DunGenExtenderEvents.cs" />
|
||||
<Compile Include="Collections\ExtenderEvent.cs" />
|
||||
<Compile Include="Collections\NodeArchetype.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" />
|
||||
<Compile Include="DunGenExtender.cs" />
|
||||
<Compile Include="Collections\DunGenExtenderProperties.cs" />
|
||||
<Compile Include="Generation\DunGenPlusGenerator.cs" />
|
||||
<Compile Include="Managers\DoorwayManager.cs" />
|
||||
<Compile Include="Patches\DoorwayConnectionPatch.cs" />
|
||||
<Compile Include="Generation\DoorwaySistersRule.cs" />
|
||||
<Compile Include="Patches\DungeonGeneratorPatch.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utils\TranspilerUtilities.cs" />
|
||||
<Compile Include="Utils\Utility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy "$(TargetPath)" "C:\Users\Jose Garcia\AppData\Roaming\r2modmanPlus-local\LethalCompany\profiles\SDM Debug\BepInEx\plugins\Unknown-DunGenPlus\$(TargetName).dll"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
90
DunGenPlus/DunGenPlus/Generation/DoorwaySistersRule.cs
Normal file
90
DunGenPlus/DunGenPlus/Generation/DoorwaySistersRule.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using DunGen;
|
||||
using DunGenPlus.Components;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DunGenPlus.Generation {
|
||||
|
||||
internal static class DoorwaySistersRule {
|
||||
|
||||
public class Data {
|
||||
public DoorwaySisters info;
|
||||
public List<DoorwayProxy> proxies;
|
||||
}
|
||||
|
||||
public static Dictionary<Doorway, Data> doorwayDictionary;
|
||||
public static Dictionary<DoorwayProxy, Data> doorwayProxyDictionary;
|
||||
|
||||
public static void UpdateCache(IEnumerable<DoorwayProxy> list){
|
||||
if (!DunGenPlusGenerator.Active || !DunGenPlusGenerator.Properties.UseDoorwaySisters) return;
|
||||
|
||||
Plugin.logger.LogInfo("Updating DoorwayProxy cache for DoorwaySistersRule");
|
||||
doorwayDictionary = new Dictionary<Doorway, Data>();
|
||||
doorwayProxyDictionary = new Dictionary<DoorwayProxy, Data>();
|
||||
|
||||
foreach(var a in list){
|
||||
|
||||
var doorway = a.DoorwayComponent;
|
||||
if (doorwayDictionary.TryGetValue(doorway, out var data)){
|
||||
|
||||
data.proxies.Add(a);
|
||||
doorwayProxyDictionary.Add(a, data);
|
||||
|
||||
} else {
|
||||
|
||||
var proxies = new List<DoorwayProxy>();
|
||||
proxies.Add(a);
|
||||
var item = new Data {
|
||||
info = doorway.GetComponent<DoorwaySisters>(),
|
||||
proxies = proxies
|
||||
};
|
||||
|
||||
doorwayProxyDictionary.Add(a, item);
|
||||
doorwayDictionary.Add(a.DoorwayComponent, item);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CanDoorwaysConnect(bool result, TileProxy tileA, TileProxy tileB, DoorwayProxy doorwayA, DoorwayProxy doorwayB){
|
||||
|
||||
if (!result) return false;
|
||||
if (!DunGenPlusGenerator.Active || !DunGenPlusGenerator.Properties.UseDoorwaySisters) return true;
|
||||
|
||||
var infoA = doorwayProxyDictionary[doorwayA].info;
|
||||
var infoB = doorwayProxyDictionary[doorwayB].info;
|
||||
|
||||
// deny if any sister doorway is already in use
|
||||
// cause it feels like dumb otherwise
|
||||
if (CheckIfSisterActive(infoA, tileB)){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CheckIfSisterActive(infoB, tileA)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// allow like normal
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CheckIfSisterActive(DoorwaySisters info, TileProxy targetTile){
|
||||
if (info == null || info.sisters == null) return false;
|
||||
|
||||
foreach(var sis in info.sisters){
|
||||
var proxies = doorwayDictionary[sis].proxies;
|
||||
foreach(var proxy in proxies){
|
||||
var result = proxy.ConnectedDoorway != null && proxy.ConnectedDoorway.TileProxy == targetTile;
|
||||
if (result) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
346
DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs
Normal file
346
DunGenPlus/DunGenPlus/Generation/DunGenPlusGenerator.cs
Normal file
|
@ -0,0 +1,346 @@
|
|||
using DunGen.Graph;
|
||||
using DunGen;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using DunGenPlus.Collections;
|
||||
using DunGenPlus.Components;
|
||||
using System.Security.Permissions;
|
||||
using DunGenPlus.Managers;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.HighDefinition;
|
||||
using BepInEx.Logging;
|
||||
using static UnityEngine.Rendering.HighDefinition.ScalableSettingLevelParameter;
|
||||
|
||||
[assembly: SecurityPermission( SecurityAction.RequestMinimum, SkipVerification = true )]
|
||||
namespace DunGenPlus.Generation {
|
||||
internal class DunGenPlusGenerator {
|
||||
public static DunGenExtender Instance { get; internal set; }
|
||||
public static DunGenExtenderProperties Properties { get; internal set; }
|
||||
public static bool Active { get; internal set; }
|
||||
public static bool ActiveAlternative { get; internal set; }
|
||||
|
||||
internal static HDRenderPipelineAsset previousHDRPAsset;
|
||||
internal static HDRenderPipelineAsset newHDRPAsset;
|
||||
|
||||
public static void Activate(DungeonGenerator generator, DunGenExtender extender){
|
||||
Instance = extender;
|
||||
Active = true;
|
||||
ActiveAlternative = true;
|
||||
|
||||
var props = extender.Properties.Copy();
|
||||
Instance.Events.OnModifyDunGenExtenderProperties.Invoke(props);
|
||||
props.SetupProperties(generator);
|
||||
Properties = props;
|
||||
|
||||
if (Properties.UseDungeonBounds) {
|
||||
generator.RestrictDungeonToBounds = Properties.UseDungeonBounds;
|
||||
var bounds = Properties.GetDungeonBounds(generator.LengthMultiplier);
|
||||
generator.TilePlacementBounds = bounds;
|
||||
Plugin.logger.LogInfo($"Dungeon Bounds: {bounds}");
|
||||
}
|
||||
|
||||
if (Properties.UseMaxShadowsRequestUpdate) {
|
||||
Plugin.logger.LogInfo($"Updating HDRP asset: setting max shadows request to {Properties.MaxShadowsRequestAmount}");
|
||||
try {
|
||||
previousHDRPAsset = QualitySettings.renderPipeline as HDRenderPipelineAsset;
|
||||
newHDRPAsset = ScriptableObject.Instantiate(previousHDRPAsset);
|
||||
|
||||
var settings = newHDRPAsset.currentPlatformRenderPipelineSettings;
|
||||
settings.hdShadowInitParams.maxScreenSpaceShadowSlots = Properties.MaxShadowsRequestAmount;
|
||||
newHDRPAsset.currentPlatformRenderPipelineSettings = settings;
|
||||
|
||||
QualitySettings.renderPipeline = newHDRPAsset;
|
||||
} catch (Exception e) {
|
||||
Plugin.logger.LogError("Failed to update HDRP asset");
|
||||
Plugin.logger.LogError(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DoorwayManager.ResetList();
|
||||
}
|
||||
|
||||
public static void Deactivate(){
|
||||
Instance = null;
|
||||
Properties = null;
|
||||
Active = false;
|
||||
ActiveAlternative = false;
|
||||
|
||||
if (previousHDRPAsset && QualitySettings.renderPipeline == newHDRPAsset) {
|
||||
Plugin.logger.LogInfo("Restoring original HDRP asset");
|
||||
|
||||
QualitySettings.renderPipeline = previousHDRPAsset;
|
||||
previousHDRPAsset = null;
|
||||
newHDRPAsset = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerator GenerateAlternativeMainPaths(DungeonGenerator gen) {
|
||||
|
||||
var altCount = Properties.MainPathCount - 1;
|
||||
|
||||
// default behaviour in case the multiple main paths are not considered
|
||||
if (!Active) {
|
||||
ActiveAlternative = false;
|
||||
yield return gen.Wait(gen.GenerateBranchPaths());
|
||||
ActiveAlternative = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (altCount <= 0) {
|
||||
Plugin.logger.LogInfo($"Switching to default dungeon branch generation due to MainPathCount being {altCount + 1}");
|
||||
ActiveAlternative = false;
|
||||
yield return gen.Wait(gen.GenerateBranchPaths());
|
||||
ActiveAlternative = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (Properties.MainRoomTilePrefab == null) {
|
||||
Plugin.logger.LogWarning($"Switching to default dungeon branch generation due to MainRoomTilePrefab being null");
|
||||
ActiveAlternative = false;
|
||||
yield return gen.Wait(gen.GenerateBranchPaths());
|
||||
ActiveAlternative = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
var allMainPathTiles = new List<List<TileProxy>>();
|
||||
allMainPathTiles.Add(gen.proxyDungeon.MainPathTiles.ToList());
|
||||
|
||||
// main room is the true main room and not the fake room
|
||||
// this MUST have multiple doorways as you can imagine
|
||||
var mainRoom = gen.proxyDungeon.MainPathTiles.FirstOrDefault(t => t.Prefab == Properties.MainRoomTilePrefab);
|
||||
if (mainRoom == null) {
|
||||
Plugin.logger.LogWarning($"Switching to default dungeon branch generation due to MainRoomTilePrefab not spawning on the main path");
|
||||
ActiveAlternative = false;
|
||||
yield return gen.Wait(gen.GenerateBranchPaths());
|
||||
ActiveAlternative = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
var doorwayGroups = mainRoom.Prefab.GetComponentInChildren<MainRoomDoorwayGroups>();
|
||||
|
||||
// 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 startingNodeIndex = nodesSorted.FindIndex(n => n.TileSets.SelectMany(t => t.TileWeights.Weights).Any(t => t.Value == Properties.MainRoomTilePrefab));
|
||||
if (startingNodeIndex == -1) {
|
||||
Plugin.logger.LogWarning($"Switching to default dungeon branch generation due to MainRoomTilePrefab not existing in the Nodes' tilesets");
|
||||
ActiveAlternative = false;
|
||||
yield return gen.Wait(gen.GenerateBranchPaths());
|
||||
ActiveAlternative = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
//FixDoorwaysToAllFloors(mainRoom, doorwayGroups);
|
||||
|
||||
gen.ChangeStatus(GenerationStatus.MainPath);
|
||||
|
||||
for (var b = 0; b < altCount; ++b) {
|
||||
RandomizeLineArchetypes(gen, true);
|
||||
var previousTile = mainRoom;
|
||||
var targetLength = Mathf.RoundToInt(gen.DungeonFlow.Length.GetRandom(gen.RandomStream) * gen.LengthMultiplier);
|
||||
var archetypes = new List<DungeonArchetype>(targetLength);
|
||||
|
||||
var newMainPathTiles = new List<TileProxy>();
|
||||
newMainPathTiles.Add(mainRoom);
|
||||
|
||||
var nodes = nodesSorted.Skip(startingNodeIndex + 1);
|
||||
var nodesVisited = new List<GraphNode>(nodes.Count());
|
||||
|
||||
// most of this code is a mix of the GenerateMainPath()
|
||||
// and GenerateBranch() code
|
||||
for(var t = 1; t < targetLength; ++t){
|
||||
var lineDepthRatio = Mathf.Clamp01((float)t / (targetLength - 1));
|
||||
var lineAtDepth = gen.DungeonFlow.GetLineAtDepth(lineDepthRatio);
|
||||
if (lineAtDepth == null){
|
||||
yield return gen.Wait(gen.InnerGenerate(true));
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (lineAtDepth != gen.previousLineSegment){
|
||||
gen.currentArchetype = lineAtDepth.GetRandomArchetype(gen.RandomStream, archetypes);
|
||||
gen.previousLineSegment = lineAtDepth;
|
||||
}
|
||||
|
||||
// terrible solution but FUCK it
|
||||
// and yet it worked
|
||||
// this is how my last node cannot be a target of pruning
|
||||
GraphNode graphNode = null;
|
||||
DungeonArchetype archetype = null;
|
||||
foreach(var g in nodes) {
|
||||
if (lineDepthRatio >= g.Position && !nodesVisited.Contains(g)) {
|
||||
graphNode = g;
|
||||
nodesVisited.Add(g);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<TileSet> useableTileSets;
|
||||
if (graphNode != null) {
|
||||
archetype = ModifyMainBranchNodeArchetype(null, graphNode, gen.RandomStream);
|
||||
useableTileSets = graphNode.TileSets;
|
||||
} else {
|
||||
archetype = gen.currentArchetype;
|
||||
useableTileSets = archetype.TileSets;
|
||||
}
|
||||
|
||||
// places fake doorways at the first node
|
||||
if (doorwayGroups && t == 1){
|
||||
foreach(var d in mainRoom.UsedDoorways) {
|
||||
if (d.ConnectedDoorway.Index != int.MaxValue) {
|
||||
var groups = doorwayGroups.GrabDoorwayGroup(d.DoorwayComponent);
|
||||
if (groups == null) continue;
|
||||
|
||||
foreach(var doorway in mainRoom.UnusedDoorways){
|
||||
if (groups.Contains(doorway.DoorwayComponent)){
|
||||
doorway.ConnectedDoorway = fakeDoorwayProxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tileProxy = gen.AddTile(previousTile, useableTileSets, lineDepthRatio, archetype, TilePlacementResult.None);
|
||||
|
||||
if (tileProxy == null) {
|
||||
Plugin.logger.LogInfo($"Alt. main branch gen failed at {b}:{lineDepthRatio}");
|
||||
yield return gen.Wait(gen.InnerGenerate(true));
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (lineDepthRatio >= 1f){
|
||||
Plugin.logger.LogInfo($"Alt. main branch at {b} ended with {tileProxy.PrefabTile.name}");
|
||||
}
|
||||
|
||||
tileProxy.Placement.BranchDepth = t;
|
||||
tileProxy.Placement.NormalizedBranchDepth = lineDepthRatio;
|
||||
|
||||
if (graphNode != null) {
|
||||
tileProxy.Placement.GraphNode = graphNode;
|
||||
tileProxy.Placement.GraphLine = null;
|
||||
} else {
|
||||
tileProxy.Placement.GraphNode = null;
|
||||
tileProxy.Placement.GraphLine = lineAtDepth;
|
||||
}
|
||||
|
||||
previousTile = tileProxy;
|
||||
newMainPathTiles.Add(tileProxy);
|
||||
|
||||
if (gen.ShouldSkipFrame(true)) yield return gen.GetRoomPause();
|
||||
}
|
||||
|
||||
allMainPathTiles.Add(newMainPathTiles);
|
||||
|
||||
}
|
||||
|
||||
// okay lets fix the fakes
|
||||
foreach(var doorway in mainRoom.UsedDoorways){
|
||||
if (doorway.ConnectedDoorway.Index == int.MaxValue) {
|
||||
doorway.ConnectedDoorway = null;
|
||||
}
|
||||
}
|
||||
|
||||
ActiveAlternative = false;
|
||||
Plugin.logger.LogInfo($"Created {altCount} alt. paths, creating branches now");
|
||||
gen.ChangeStatus(GenerationStatus.Branching);
|
||||
|
||||
// this is major trickery and it works still
|
||||
for(var b = 0; b < altCount + 1; ++b){
|
||||
Plugin.logger.LogInfo($"Branch {b}");
|
||||
RandomizeLineArchetypes(gen, false);
|
||||
gen.proxyDungeon.MainPathTiles = allMainPathTiles[b];
|
||||
yield return gen.Wait(gen.GenerateBranchPaths());
|
||||
}
|
||||
|
||||
ActiveAlternative = true;
|
||||
|
||||
gen.proxyDungeon.MainPathTiles = allMainPathTiles[0];
|
||||
}
|
||||
|
||||
public static void RandomizeLineArchetypes(DungeonGenerator gen, bool randomizeMainPath){
|
||||
if (!Properties.UseLineRandomizer) return;
|
||||
|
||||
var flow = Instance.DungeonFlow;
|
||||
var lines = flow.Lines;
|
||||
var tilesetsUsed = new Dictionary<TileSet, int>();
|
||||
foreach(var t in Properties.LineRandomizerTileSets){
|
||||
tilesetsUsed.Add(t, 0);
|
||||
}
|
||||
|
||||
foreach(var a in Properties.LineRandomizerArchetypes) {
|
||||
var tiles = randomizeMainPath ? a.TileSets : a.BranchCapTileSets;
|
||||
RandomizeArchetype(gen, tiles, tilesetsUsed);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RandomizeArchetype(DungeonGenerator gen, List<TileSet> targetTileSet, Dictionary<TileSet, int> tilesetsUsed){
|
||||
// get 3 random
|
||||
var newTiles = Properties.LineRandomizerTileSets
|
||||
.OrderBy(t => tilesetsUsed[t] + gen.RandomStream.NextDouble())
|
||||
.Take(Properties.LineRandomizerTakeCount);
|
||||
|
||||
var i = targetTileSet.Count - 1;
|
||||
foreach(var n in newTiles){
|
||||
targetTileSet[i] = n;
|
||||
--i;
|
||||
|
||||
tilesetsUsed[n] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static DungeonArchetype ModifyMainBranchNodeArchetype(DungeonArchetype archetype, GraphNode node, RandomStream randomStream){
|
||||
if (!DunGenPlusGenerator.Active) return archetype;
|
||||
|
||||
if (Properties.AddArchetypesToNormalNodes && node.NodeType == NodeType.Normal) {
|
||||
return Properties.GetRandomArchetype(node.Label, randomStream);;
|
||||
}
|
||||
return archetype;
|
||||
}
|
||||
|
||||
public static void FixDoorwaysToAllFloors(TileProxy mainRoom, MainRoomDoorwayGroups doorwayGroups) {
|
||||
var first = doorwayGroups.doorwayListFirst;
|
||||
if (first == null) return;
|
||||
|
||||
foreach(var target in mainRoom.UsedDoorways){
|
||||
if (target.ConnectedDoorway.Index == int.MaxValue && !first.Contains(target.DoorwayComponent)) {
|
||||
target.ConnectedDoorway = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public static GraphNode ModifyGraphNode(GraphNode node) {
|
||||
if (!Patch.active) return node;
|
||||
|
||||
if (node.Label == "Hallway Entrance 1") {
|
||||
return Assets.networkObjectList.gardenEntranceGraphNode;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public static TileProxy FixTilesToAllFloors(TileProxy mainTile) {
|
||||
if (!Patch.active) return mainTile;
|
||||
|
||||
var groups = mainTile.Prefab.GetComponentInChildren<MainRoomDoorwayGroups>();
|
||||
var first = groups.groupFirst;
|
||||
|
||||
foreach(var target in mainTile.doorways){
|
||||
if (target.ConnectedDoorway != null && target.ConnectedDoorway.Index == int.MaxValue && !first.Contains(target.DoorwayComponent)) {
|
||||
target.ConnectedDoorway = null;
|
||||
}
|
||||
}
|
||||
|
||||
return mainTile;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
46
DunGenPlus/DunGenPlus/Managers/DoorwayManager.cs
Normal file
46
DunGenPlus/DunGenPlus/Managers/DoorwayManager.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using DunGen.Adapters;
|
||||
using DunGenPlus.Components;
|
||||
using DunGenPlus.Generation;
|
||||
using DunGenPlus.Utils;
|
||||
|
||||
namespace DunGenPlus.Managers {
|
||||
public static class DoorwayManager {
|
||||
|
||||
public static ActionList onMainEntranceTeleportSpawnedEvent = new ActionList("onMainEntranceTeleportSpawned");
|
||||
public static List<DoorwayCleanup> doorwayCleanupList;
|
||||
|
||||
public static void ResetList(){
|
||||
doorwayCleanupList = new List<DoorwayCleanup>();
|
||||
}
|
||||
|
||||
public static void AddDoorwayCleanup(DoorwayCleanup cleanup){
|
||||
doorwayCleanupList.Add(cleanup);
|
||||
}
|
||||
|
||||
public static void onMainEntranceTeleportSpawnedFunction(){
|
||||
if (DunGenPlusGenerator.Active) {
|
||||
foreach(var d in doorwayCleanupList){
|
||||
d.SetBlockers(false);
|
||||
d.Cleanup();
|
||||
}
|
||||
|
||||
try{
|
||||
var dungeonGen = RoundManager.Instance.dungeonGenerator;
|
||||
var navmesh = dungeonGen.transform.parent.GetComponentInChildren<UnityNavMeshAdapter>();
|
||||
navmesh.Run(dungeonGen.Generator);
|
||||
Plugin.logger.LogInfo("Rebuild nav mesh");
|
||||
} catch (Exception e){
|
||||
Plugin.logger.LogError("Failed to rebuild nav mesh");
|
||||
Plugin.logger.LogError(e.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
63
DunGenPlus/DunGenPlus/Patches/DoorwayConnectionPatch.cs
Normal file
63
DunGenPlus/DunGenPlus/Patches/DoorwayConnectionPatch.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using DunGen;
|
||||
using DunGenPlus.Utils;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using DunGenPlus.Generation;
|
||||
|
||||
namespace DunGenPlus.Patches {
|
||||
internal class DoorwayConnectionPatch {
|
||||
|
||||
[HarmonyPatch(typeof(DungeonProxy), "ConnectOverlappingDoorways")]
|
||||
[HarmonyPrefix]
|
||||
public static void ConnectOverlappingDoorwaysPrePatch(ref DungeonProxy __instance){
|
||||
var enumerable = __instance.AllTiles.SelectMany(t => t.Doorways);
|
||||
DoorwaySistersRule.UpdateCache(enumerable);
|
||||
}
|
||||
|
||||
|
||||
[HarmonyTranspiler]
|
||||
[HarmonyPatch(typeof(DungeonProxy), "ConnectOverlappingDoorways")]
|
||||
public static IEnumerable<CodeInstruction> ConnectOverlappingDoorwaysPatch(IEnumerable<CodeInstruction> instructions){
|
||||
var callFunction = typeof(DunGen.Graph.DungeonFlow).GetMethod("CanDoorwaysConnect", BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
var sequence = new InstructionSequence("doorway connect", false);
|
||||
sequence.AddBasic(OpCodes.Callvirt, callFunction);
|
||||
sequence.AddBasic(OpCodes.Brfalse);
|
||||
|
||||
foreach(var instruction in instructions){
|
||||
|
||||
if (sequence.VerifyStage(instruction)){
|
||||
|
||||
var method = typeof(DoorwaySistersRule).GetMethod("CanDoorwaysConnect", BindingFlags.Static | BindingFlags.Public);
|
||||
var getTileProxy = typeof(DoorwayProxy).GetMethod("get_TileProxy", BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_2);
|
||||
yield return new CodeInstruction(OpCodes.Callvirt, getTileProxy);
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_S, 4);
|
||||
yield return new CodeInstruction(OpCodes.Callvirt, getTileProxy);
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_2);
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_S, 4);
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Call, method);
|
||||
|
||||
yield return instruction;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return instruction;
|
||||
}
|
||||
|
||||
sequence.ReportComplete();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
162
DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs
Normal file
162
DunGenPlus/DunGenPlus/Patches/DungeonGeneratorPatch.cs
Normal file
|
@ -0,0 +1,162 @@
|
|||
using DunGen;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using DunGenPlus.Utils;
|
||||
using DunGenPlus.Generation;
|
||||
using DunGenPlus.Managers;
|
||||
|
||||
namespace DunGenPlus.Patches {
|
||||
internal class DungeonGeneratorPatch {
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath")]
|
||||
public static void GenerateMainPathPatch(ref DungeonGenerator __instance, ref IEnumerator __result){
|
||||
if (DunGenPlusGenerator.Active && DunGenPlusGenerator.ActiveAlternative) {
|
||||
DunGenPlusGenerator.RandomizeLineArchetypes(__instance, true);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DungeonGenerator), "GenerateBranchPaths")]
|
||||
public static void GenerateBranchPathsPatch(ref DungeonGenerator __instance, ref IEnumerator __result){
|
||||
if (DunGenPlusGenerator.Active && DunGenPlusGenerator.ActiveAlternative) {
|
||||
__result = DunGenPlusGenerator.GenerateAlternativeMainPaths(__instance);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyTranspiler]
|
||||
[HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath", MethodType.Enumerator)]
|
||||
public static IEnumerable<CodeInstruction> GenerateMainPathPatch(IEnumerable<CodeInstruction> instructions){
|
||||
|
||||
var addArchFunction = typeof(List<DungeonArchetype>).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
var archSequence = new InstructionSequence("archetype node");
|
||||
archSequence.AddOperandTypeCheck(OpCodes.Ldfld, typeof(List<DungeonArchetype>));
|
||||
archSequence.AddBasic(OpCodes.Ldnull);
|
||||
archSequence.AddBasic(OpCodes.Callvirt, addArchFunction);
|
||||
|
||||
foreach(var instruction in instructions){
|
||||
|
||||
if (archSequence.VerifyStage(instruction)){
|
||||
|
||||
var randomStreamMethod = typeof(DungeonGenerator).GetMethod("get_RandomStream", BindingFlags.Public | BindingFlags.Instance);
|
||||
var modifyMethod = typeof(DunGenPlusGenerator).GetMethod("ModifyMainBranchNodeArchetype", BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_S, 8);
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_1);
|
||||
yield return new CodeInstruction(OpCodes.Call, randomStreamMethod);
|
||||
yield return new CodeInstruction(OpCodes.Call, modifyMethod);
|
||||
yield return instruction;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return instruction;
|
||||
}
|
||||
|
||||
archSequence.ReportComplete();
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(RoundManager), "FinishGeneratingLevel")]
|
||||
public static void GenerateBranchPathsPatch(){
|
||||
if (DunGenPlusGenerator.Active) {
|
||||
Plugin.logger.LogInfo("Alt. InnerGenerate() function complete");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(RoundManager), "SetPowerOffAtStart")]
|
||||
public static void SetPowerOffAtStartPatch(){
|
||||
DoorwayManager.onMainEntranceTeleportSpawnedEvent.Call();
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DungeonGenerator), "PostProcess")]
|
||||
public static void GenerateBranchPathsPatch(ref DungeonGenerator __instance){
|
||||
if (DunGenPlusGenerator.Active) {
|
||||
var value = __instance.RandomStream.Next(999);
|
||||
Components.Props.SpawnSyncedObjectCycle.UpdateCycle(value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
[HarmonyTranspiler]
|
||||
[HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath", MethodType.Enumerator)]
|
||||
public static IEnumerable<CodeInstruction> GenerateMainPathPatch(IEnumerable<CodeInstruction> instructions){
|
||||
|
||||
var addArchFunction = typeof(List<DungeonArchetype>).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
|
||||
//var addNodeFunction = typeof(List<GraphNode>).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
var archSequence = new InstructionSequence("archetype node");
|
||||
archSequence.AddOperandTypeCheck(OpCodes.Ldfld, typeof(List<DungeonArchetype>));
|
||||
archSequence.AddBasic(OpCodes.Ldnull);
|
||||
archSequence.AddBasic(OpCodes.Callvirt, addArchFunction);
|
||||
|
||||
var nodeSequence = new InstructionSequence("graph node");
|
||||
nodeSequence.AddBasicLocal(OpCodes.Ldloc_S, 12);
|
||||
nodeSequence.AddBasicLocal(OpCodes.Stloc_S, 8);
|
||||
|
||||
var limitSequence = new InstructionSequence("limit nodes");
|
||||
limitSequence.AddBasic(OpCodes.Ldnull);
|
||||
limitSequence.AddBasicLocal(OpCodes.Stloc_S, 13);
|
||||
limitSequence.AddBasic(OpCodes.Ldloc_1);
|
||||
limitSequence.AddBasicLocal(OpCodes.Ldloc_S, 13);
|
||||
|
||||
foreach(var instruction in instructions){
|
||||
|
||||
if (archSequence.VerifyStage(instruction)){
|
||||
|
||||
var method = typeof(GeneratePath).GetMethod("ModifyMainBranchNodeArchetype", BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_S, 8);
|
||||
yield return new CodeInstruction(OpCodes.Call, method);
|
||||
yield return instruction;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (nodeSequence.VerifyStage(instruction)){
|
||||
|
||||
var method = typeof(GeneratePath).GetMethod("ModifyGraphNode", BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Call, method);
|
||||
yield return instruction;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (limitSequence.VerifyStage(instruction)){
|
||||
|
||||
var method = typeof(GeneratePath).GetMethod("LimitTilesToFirstFloor", BindingFlags.Public | BindingFlags.Static);
|
||||
var field = typeof(DungeonGenerator).Assembly.GetType("DunGen.DungeonGenerator+<GenerateMainPath>d__100").GetField("<j>5__8", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
yield return instruction;
|
||||
yield return new CodeInstruction(OpCodes.Ldarg_0);
|
||||
yield return new CodeInstruction(OpCodes.Ldfld, field);
|
||||
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Call, method);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return instruction;
|
||||
}
|
||||
|
||||
archSequence.ReportComplete();
|
||||
nodeSequence.ReportComplete();
|
||||
limitSequence.ReportComplete();
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
67
DunGenPlus/DunGenPlus/Plugin.cs
Normal file
67
DunGenPlus/DunGenPlus/Plugin.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using BepInEx;
|
||||
using BepInEx.Logging;
|
||||
using DunGen;
|
||||
using DunGen.Graph;
|
||||
using DunGenPlus.Generation;
|
||||
using DunGenPlus.Managers;
|
||||
using DunGenPlus.Patches;
|
||||
using HarmonyLib;
|
||||
using LethalLevelLoader;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace DunGenPlus {
|
||||
|
||||
[BepInPlugin(modGUID, modName, modVersion)]
|
||||
[BepInProcess("Lethal Company.exe")]
|
||||
public class Plugin : BaseUnityPlugin {
|
||||
|
||||
internal const string modGUID = "ImoutoSama.DungeonGenerationPlus";
|
||||
private const string modName = "Dungeon Generation Plus";
|
||||
private const string modVersion = "1.0.0";
|
||||
|
||||
internal readonly Harmony Harmony = new Harmony(modGUID);
|
||||
|
||||
internal static Plugin Instance {get; private set;}
|
||||
|
||||
internal static ManualLogSource logger { get; private set; }
|
||||
|
||||
internal static Dictionary<DungeonFlow, DunGenExtender> DunGenExtenders = new Dictionary<DungeonFlow, DunGenExtender>();
|
||||
|
||||
void Awake() {
|
||||
if (Instance == null) Instance = this;
|
||||
|
||||
logger = BepInEx.Logging.Logger.CreateLogSource(modGUID);
|
||||
logger.LogInfo($"Plugin {modName} has been added!");
|
||||
|
||||
Harmony.PatchAll(typeof(DungeonGeneratorPatch));
|
||||
Harmony.PatchAll(typeof(DoorwayConnectionPatch));
|
||||
|
||||
Assets.LoadAssets();
|
||||
DungeonManager.GlobalDungeonEvents.onBeforeDungeonGenerate.AddListener(OnDunGenExtenderLoad);
|
||||
DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction);
|
||||
}
|
||||
|
||||
void OnDunGenExtenderLoad(RoundManager roundManager) {
|
||||
DunGenPlusGenerator.Deactivate();
|
||||
|
||||
var generator = roundManager.dungeonGenerator.Generator;
|
||||
var flow = generator.DungeonFlow;
|
||||
if (DunGenExtenders.TryGetValue(flow, out var value)) {
|
||||
Plugin.logger.LogInfo($"Loading DunGenExtender for {flow.name}");
|
||||
DunGenPlusGenerator.Activate(generator, value);
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.logger.LogInfo($"Did not load a DunGenExtenderer");
|
||||
DunGenPlusGenerator.Deactivate();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
36
DunGenPlus/DunGenPlus/Properties/AssemblyInfo.cs
Normal file
36
DunGenPlus/DunGenPlus/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// アセンブリに関する一般情報は以下を通して制御されます
|
||||
// 制御されます。アセンブリに関連付けられている情報を変更するには、
|
||||
// これらの属性値を変更してください。
|
||||
[assembly: AssemblyTitle("DunGenPlus")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DunGenPlus")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2024")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから
|
||||
// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、
|
||||
// その型の ComVisible 属性を true に設定してください。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります
|
||||
[assembly: Guid("13cde60e-1975-463b-9da1-ccb3f3ebabd8")]
|
||||
|
||||
// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
|
||||
//
|
||||
// メジャー バージョン
|
||||
// マイナー バージョン
|
||||
// ビルド番号
|
||||
// リビジョン
|
||||
//
|
||||
// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます
|
||||
// 既定値にすることができます:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
191
DunGenPlus/DunGenPlus/Utils/TranspilerUtilities.cs
Normal file
191
DunGenPlus/DunGenPlus/Utils/TranspilerUtilities.cs
Normal file
|
@ -0,0 +1,191 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using HarmonyLib;
|
||||
using GameNetcodeStuff;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using BepInEx.Logging;
|
||||
using UnityEngine;
|
||||
using DunGenPlus;
|
||||
|
||||
namespace DunGenPlus.Utils {
|
||||
|
||||
internal class InjectionDictionary {
|
||||
|
||||
public string name;
|
||||
public List<CodeInstruction> instructions;
|
||||
public CodeInstruction[] injections;
|
||||
|
||||
int counter;
|
||||
|
||||
public InjectionDictionary(string name, MethodInfo methodInjection, params CodeInstruction[] instructions) {
|
||||
this.name = name;
|
||||
this.injections = new CodeInstruction[] { new CodeInstruction(OpCodes.Call, methodInjection) } ;
|
||||
this.instructions = instructions.ToList();
|
||||
}
|
||||
|
||||
public InjectionDictionary(string name, CodeInstruction[] codeInjections, params CodeInstruction[] instructions) {
|
||||
this.name = name;
|
||||
this.injections = codeInjections;
|
||||
this.instructions = instructions.ToList();
|
||||
}
|
||||
|
||||
public void ResetCounter(){
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
public void AddCounter() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
public void Report(string debugFunction, int? expectedCounter){
|
||||
if (counter == 0) {
|
||||
Plugin.logger.LogError($"{debugFunction} could not inject {name}. Probably scary");
|
||||
} else if (!expectedCounter.HasValue) {
|
||||
Plugin.logger.LogInfo($"{debugFunction} inject {name} {counter} time(s)");
|
||||
} else if (expectedCounter.Value != counter){
|
||||
Plugin.logger.LogWarning($"{debugFunction} inject {name} {counter} time(s) (Expected {expectedCounter.Value}). Probably not an error but be warned");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class InstructionSequence {
|
||||
|
||||
public static ManualLogSource logger => Plugin.logger;
|
||||
|
||||
List<Func<CodeInstruction, bool>> seq;
|
||||
string name;
|
||||
string extraErrorMessage;
|
||||
int stage = 0;
|
||||
bool completed = false;
|
||||
bool single;
|
||||
|
||||
public InstructionSequence(string name, bool single = true, string extraErrorMessage = default(string)){
|
||||
this.name = name;
|
||||
this.single = single;
|
||||
this.extraErrorMessage = extraErrorMessage;
|
||||
seq = new List<Func<CodeInstruction, bool>>();
|
||||
}
|
||||
|
||||
public void Add(Func<CodeInstruction, bool> next){
|
||||
seq.Add(next);
|
||||
}
|
||||
|
||||
public void AddBasic(OpCode opcode){
|
||||
seq.Add((i) => i.opcode == opcode);
|
||||
}
|
||||
|
||||
public void AddBasic(OpCode opcode, object operand){
|
||||
seq.Add((i) => i.opcode == opcode && i.operand == operand);
|
||||
}
|
||||
|
||||
public void AddBasicLocal(OpCode opcode, int operand){
|
||||
seq.Add((i) => i.opcode == opcode && (i.operand as LocalBuilder).LocalIndex == operand);
|
||||
}
|
||||
|
||||
public void AddOperandTypeCheck(OpCode opcode, Type operandType){
|
||||
seq.Add((i) => {
|
||||
var fieldInfo = i.operand as FieldInfo;
|
||||
if (i.opcode == opcode && fieldInfo != null) {
|
||||
return fieldInfo.FieldType == operandType;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void AddBasicWithAlternateMethodName(OpCode opcode, object operand, string methodName){
|
||||
seq.Add((i) => {
|
||||
if (i.opcode == opcode && i.operand == operand) return true;
|
||||
|
||||
var mth = i.operand as MethodInfo;
|
||||
if (mth != null && mth.Name == methodName) return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void AddSpecial(OpCode opcode, Func<CodeInstruction, bool> extra){
|
||||
seq.Add((i) => i.opcode == opcode && extra.Invoke(i));
|
||||
}
|
||||
|
||||
public void AddQuickInjection(MethodInfo methodInfo){
|
||||
|
||||
}
|
||||
|
||||
public bool VerifyStage(CodeInstruction current){
|
||||
var s = seq[stage];
|
||||
if (s.Invoke(current)) {
|
||||
//Plugin.logger.LogInfo($"{name}({stage}): current.ToString()");
|
||||
stage++;
|
||||
} else {
|
||||
stage = 0;
|
||||
}
|
||||
|
||||
if (stage >= seq.Count){
|
||||
|
||||
if (completed && single){
|
||||
throw new Exception($"Found multiple valid {name} instructions");
|
||||
}
|
||||
|
||||
stage = 0;
|
||||
completed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ReportComplete(){
|
||||
if (completed == false){
|
||||
var errorM = string.IsNullOrWhiteSpace(extraErrorMessage) ? "BIG PROBLEM!" : extraErrorMessage;
|
||||
logger.LogError($"HarmonyTranspiler for {name} has failed. {errorM}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class TranspilerUtilities {
|
||||
|
||||
public static IEnumerable<CodeInstruction> InjectMethod(IEnumerable<CodeInstruction> instructions, InjectionDictionary injection, string debugFunction, int? expectedCounter = default){
|
||||
var targets = injection.instructions;
|
||||
var codeInjections = injection.injections;
|
||||
injection.ResetCounter();
|
||||
|
||||
foreach(var i in instructions){
|
||||
foreach(var t in targets){
|
||||
if (i.opcode == t.opcode && i.operand == t.operand){
|
||||
yield return i;
|
||||
foreach(var c in codeInjections) yield return c;
|
||||
injection.AddCounter();
|
||||
goto GoNext;
|
||||
}
|
||||
}
|
||||
yield return i;
|
||||
|
||||
GoNext:;
|
||||
}
|
||||
|
||||
injection.Report(debugFunction, expectedCounter);
|
||||
}
|
||||
|
||||
public static bool IsInstructionNearFloatValue(CodeInstruction instruction, float value){
|
||||
return Mathf.Abs((float)instruction.operand - value) < 0.1f;
|
||||
}
|
||||
|
||||
public static void PrintInstructions(IEnumerable<CodeInstruction> instructions) {
|
||||
foreach(var i in instructions){
|
||||
var opString = i.opcode.ToString();
|
||||
var objString = i.operand != null ? i.operand.ToString() : "NULL";
|
||||
Plugin.logger.LogInfo($"{opString}: {objString}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
33
DunGenPlus/DunGenPlus/Utils/Utility.cs
Normal file
33
DunGenPlus/DunGenPlus/Utils/Utility.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DunGenPlus.Utils {
|
||||
public class ActionList {
|
||||
public string name;
|
||||
public List<(string name, Action action)> actionList;
|
||||
|
||||
public ActionList(string name){
|
||||
this.name = name;
|
||||
actionList = new List<(string, Action)>();
|
||||
}
|
||||
|
||||
public void AddEvent(string name, Action act){
|
||||
actionList.Add((name, act));
|
||||
}
|
||||
|
||||
public void Call(){
|
||||
foreach(var pair in actionList){
|
||||
try {
|
||||
pair.action.Invoke();
|
||||
} catch (Exception e) {
|
||||
Plugin.logger.LogError($"Error with event {name}/{pair.name}");
|
||||
Plugin.logger.LogError(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue