diff --git a/DunGenPlus/DunGenPlus/API.cs b/DunGenPlus/DunGenPlus/API.cs
index a3a00c1..45d91e1 100644
--- a/DunGenPlus/DunGenPlus/API.cs
+++ b/DunGenPlus/DunGenPlus/API.cs
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using DunGen;
using DunGen.Graph;
+using DunGenPlus.Generation;
+using LethalLevelLoader;
using UnityEngine;
namespace DunGenPlus
@@ -68,6 +71,31 @@ namespace DunGenPlus
return Plugin.DunGenExtenders.ContainsKey(dungeonFlow);
}
+ ///
+ /// Checks if has a registered .
+ ///
+ ///
+ ///
+ /// if has a registered .
+ /// otherwise.
+ ///
+ public static bool ContainsDungeonFlow(ExtendedDungeonFlow extendedDungeonFlow) {
+ if (extendedDungeonFlow == null) return false;
+ return ContainsDungeonFlow(extendedDungeonFlow.DungeonFlow);
+ }
+
+ ///
+ /// Returns corresponding for .
+ ///
+ ///
+ ///
+ public static DunGenExtender GetDunGenExtender(DungeonFlow dungeonFlow) {
+ if (Plugin.DunGenExtenders.TryGetValue(dungeonFlow, out var value)) {
+ return value;
+ }
+ return null;
+ }
+
///
/// Creates and returns an empty .
///
diff --git a/DunGenPlus/DunGenPlus/Assets.cs b/DunGenPlus/DunGenPlus/Assets.cs
index 6a16aff..7961177 100644
--- a/DunGenPlus/DunGenPlus/Assets.cs
+++ b/DunGenPlus/DunGenPlus/Assets.cs
@@ -8,18 +8,23 @@ using System.Threading.Tasks;
using BepInEx;
using UnityEngine;
using LethalLevelLoader;
+using DunGen.Graph;
+using DunGen;
+using System.Reflection;
+using Unity.Netcode;
namespace DunGenPlus {
+
internal class Assets {
-
- public static void LoadAssets(){
+
+ public static void LoadAssets() {
foreach (string text in Directory.GetFiles(Paths.PluginPath, "*.lethalbundle", SearchOption.AllDirectories)) {
- FileInfo fileInfo = new FileInfo(text);
+ FileInfo fileInfo = new FileInfo(text);
LethalLevelLoader.AssetBundleLoader.AddOnLethalBundleLoadedListener(AutoAddLethalBundle, fileInfo.Name);
- }
+ }
}
- static void AutoAddLethalBundle(AssetBundle assetBundle){
+ static void AutoAddLethalBundle(AssetBundle assetBundle) {
if (assetBundle.isStreamedSceneAssetBundle) return;
var extenders = assetBundle.LoadAllAssets();
@@ -29,10 +34,49 @@ namespace DunGenPlus {
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){
+ foreach (var e in extenders) {
API.AddDunGenExtender(e);
}
}
+ public static AssetBundle MainAssetBundle = null;
+ public static GameObject DevDebugPrefab;
+
+ public static T Load(string name, bool onlyReportErrors = true) where T : UnityEngine.Object {
+ if (MainAssetBundle == null) {
+ Plugin.logger.LogError($"Trying to load in asset but asset bundle is missing");
+ return null;
+ }
+
+ var asset = MainAssetBundle.LoadAsset(name);
+ var missingasset = asset == null;
+
+ if (missingasset || onlyReportErrors == true) {
+ Plugin.logger.LogDebug($"Loading asset {name}");
+ }
+
+ if (missingasset) {
+ Plugin.logger.LogError($"...but it was not found");
+ }
+ return asset;
+ }
+
+ public static void LoadAssetBundle() {
+ if (MainAssetBundle == null) {
+ var assembly = Assembly.GetExecutingAssembly();
+ var resourceNames = assembly.GetManifestResourceNames();
+ if (resourceNames.Length >= 1) {
+ var name = resourceNames[0];
+ using (var assetStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) {
+ Plugin.logger.LogDebug($"Loading resource {name}");
+ MainAssetBundle = AssetBundle.LoadFromStream(assetStream);
+ }
+ }
+ }
+
+ DevDebugPrefab = Load("DevDebug");
+
+ }
+
}
}
diff --git a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs
index c7218dc..2439964 100644
--- a/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs
+++ b/DunGenPlus/DunGenPlus/Collections/DunGenExtenderProperties.cs
@@ -125,45 +125,46 @@ namespace DunGenPlus.Collections {
internal Bounds GetDungeonBounds(float dungeonScale) {
var size = DungeonSizeBase + Vector3.Scale(DungeonSizeBase * (dungeonScale - 1), DungeonSizeFactor);
- var offset = DungeonPositionOffset + Vector3.Scale(size, DungeonPositionPivot);
+ var offset = DungeonPositionOffset + Vector3.Scale(size, DungeonPositionPivot - Vector3.one * 0.5f);
return new Bounds(offset, size);
}
+ internal void CopyFrom(DunGenExtenderProperties props) {
+ MainPathCount = props.MainPathCount;
+ MainRoomTilePrefab = props.MainRoomTilePrefab;
+ MainPathCopyNodeBehaviour = props.MainPathCopyNodeBehaviour;
+
+ UseDungeonBounds = props.UseDungeonBounds;
+ DungeonSizeBase = props.DungeonSizeBase;
+ DungeonSizeFactor = props.DungeonSizeFactor;
+ DungeonPositionOffset = props.DungeonPositionOffset;
+ DungeonPositionPivot = props.DungeonPositionPivot;
+
+ AddArchetypesToNormalNodes = props.AddArchetypesToNormalNodes;
+ NormalNodeArchetypes = props.NormalNodeArchetypes;
+
+ UseForcedTiles = props.UseForcedTiles;
+ ForcedTileSets = props.ForcedTileSets;
+
+ UseBranchLoopBoost = props.UseBranchLoopBoost;
+ BranchLoopBoostTileSearch = props.BranchLoopBoostTileSearch;
+ BranchLoopBoostTileScale = props.BranchLoopBoostTileScale;
+
+ UseLineRandomizer = props.UseLineRandomizer;
+ LineRandomizerTileSets = props.LineRandomizerTileSets;
+ LineRandomizerArchetypes = props.LineRandomizerArchetypes;
+ LineRandomizerTakeCount = props.LineRandomizerTakeCount;
+
+ UseMaxShadowsRequestUpdate = props.UseMaxShadowsRequestUpdate;
+ MaxShadowsRequestAmount = props.MaxShadowsRequestAmount;
+
+ UseDoorwaySisters = props.UseDoorwaySisters;
+ UseRandomGuaranteedScrapSpawn = props.UseRandomGuaranteedScrapSpawn;
+ }
+
internal DunGenExtenderProperties Copy() {
var copy = new DunGenExtenderProperties();
-
- copy.MainPathCount = MainPathCount;
- copy.MainRoomTilePrefab = MainRoomTilePrefab;
- copy.MainPathCopyNodeBehaviour = MainPathCopyNodeBehaviour;
- //copy.MainPathCopyInjectionTiles = MainPathCopyInjectionTiles;
-
- copy.UseDungeonBounds = UseDungeonBounds;
- copy.DungeonSizeBase = DungeonSizeBase;
- copy.DungeonSizeFactor = DungeonSizeFactor;
- copy.DungeonPositionOffset = DungeonPositionOffset;
- copy.DungeonPositionPivot = DungeonPositionPivot;
-
- copy.AddArchetypesToNormalNodes = AddArchetypesToNormalNodes;
- copy.NormalNodeArchetypes = NormalNodeArchetypes;
-
- copy.UseForcedTiles = UseForcedTiles;
- copy.ForcedTileSets = ForcedTileSets;
-
- copy.UseBranchLoopBoost = UseBranchLoopBoost;
- copy.BranchLoopBoostTileSearch = BranchLoopBoostTileSearch;
- copy.BranchLoopBoostTileScale = BranchLoopBoostTileScale;
-
- copy.UseLineRandomizer = UseLineRandomizer;
- copy.LineRandomizerTileSets = LineRandomizerTileSets;
- copy.LineRandomizerArchetypes = LineRandomizerArchetypes;
- copy.LineRandomizerTakeCount = LineRandomizerTakeCount;
-
- copy.UseMaxShadowsRequestUpdate = UseMaxShadowsRequestUpdate;
- copy.MaxShadowsRequestAmount = MaxShadowsRequestAmount;
-
- copy.UseDoorwaySisters = UseDoorwaySisters;
- copy.UseRandomGuaranteedScrapSpawn = UseRandomGuaranteedScrapSpawn;
-
+ copy.CopyFrom(this);
return copy;
}
diff --git a/DunGenPlus/DunGenPlus/Collections/NullObject.cs b/DunGenPlus/DunGenPlus/Collections/NullObject.cs
new file mode 100644
index 0000000..e84ce3a
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/Collections/NullObject.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DunGenPlus.Collections {
+
+ // https://stackoverflow.com/questions/4632945/why-doesnt-dictionarytkey-tvalue-support-null-key
+ internal struct NullObject where T: class{
+ public T Item;
+ private bool isNull;
+
+ public NullObject(T item, bool isNull) {
+ this.Item = item;
+ this.isNull = isNull;
+ }
+
+
+ public NullObject(T item) : this(item, item == null){
+
+ }
+
+
+ public static implicit operator T(NullObject nullObject) {
+ return nullObject.Item;
+ }
+
+ public static implicit operator NullObject(T item) {
+ return new NullObject(item);
+ }
+
+ public override string ToString() {
+ return (Item != null) ? Item.ToString() : "NULL";
+ }
+
+ public override bool Equals(object obj) {
+ if (obj == null) return isNull;
+ if (!(obj is NullObject)) return false;
+ var no = (NullObject)obj;
+ if (isNull) return no.isNull;
+ if (no.isNull) return false;
+ return Item.Equals(no.Item);
+ }
+
+ public override int GetHashCode(){
+ if (isNull) return 0;
+ var result = Item.GetHashCode();
+ if (result >= 0) result++;
+ return result;
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/DevDebugManager.cs b/DunGenPlus/DunGenPlus/DevTools/DevDebugManager.cs
new file mode 100644
index 0000000..6fcb9ce
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/DevDebugManager.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+using DunGen;
+using UnityEngine.UI;
+using TMPro;
+using DunGen.Graph;
+using LethalLevelLoader;
+using UnityEngine.InputSystem;
+using DunGenPlus.DevTools.Panels;
+using DunGenPlus.DevTools.UIElements;
+
+namespace DunGenPlus.DevTools {
+ internal partial class DevDebugManager : MonoBehaviour {
+ public static DevDebugManager Instance { get; private set; }
+
+ [Header("References")]
+ public RuntimeDungeon dungeon;
+ public GameObject devCamera;
+ public BasePanel[] panels;
+
+ public TMP_Dropdown dungeonFlowSelectionDropDown;
+ private ExtendedDungeonFlow[] dungeonFlows;
+ internal ExtendedDungeonFlow selectedDungeonFlow;
+
+ public TextMeshProUGUI statusTextMesh;
+ public TextMeshProUGUI statsTextMesh;
+
+ // fake
+ private GameObject disabledGameObject;
+ private RoundManager fakeRoundManager;
+
+ // cache
+ private Camera lastMainCamera;
+ private Vector3 lastCameraPosition;
+ private Quaternion lastCameraRotation;
+
+ void Awake(){
+ Instance = this;
+
+ Cursor.lockState = CursorLockMode.None;
+ Cursor.visible = true;
+
+ CacheMainCamera();
+ BeginDevCamera();
+ GetAllDungeonFlows();
+
+ foreach(var p in panels) p.AwakeCall();
+ OpenPanel(0);
+
+ dungeon.Generator.OnGenerationStatusChanged += OnDungeonFinished;
+
+ disabledGameObject = new GameObject("Disabled GOBJ");
+ disabledGameObject.SetActive(false);
+ disabledGameObject.transform.SetParent(transform);
+ }
+
+ void OnDestroy(){
+ Instance = null;
+
+ EndDevCamera();
+ }
+
+ void Update(){
+ statusTextMesh.text = dungeon.Generator.Status.ToString();
+
+ if (!DevDebugOpen.IsSinglePlayerInShip()) {
+ CloseDevDebug();
+ }
+
+ if (Mouse.current.middleButton.isPressed) {
+ var delta = Mouse.current.delta.value;
+ var movement = delta;
+ devCamera.transform.position += new Vector3(-movement.x, 0f, -movement.y);
+ }
+ }
+
+ public void OpenPanel(int index) {
+ for(var i = 0; i < panels.Length; ++i) {
+ panels[i].SetPanelVisibility(i == index);
+ }
+ }
+
+ public void SelectDungeonFlow(int index){
+ selectedDungeonFlow = dungeonFlows[index];
+ dungeon.Generator.DungeonFlow = selectedDungeonFlow.DungeonFlow;
+ UpdatePlusPanel();
+ Plugin.logger.LogInfo($"Selecting {selectedDungeonFlow.DungeonName}");
+ }
+
+ public void GenerateDungeon(){
+ DeleteDungeon();
+ Plugin.logger.LogInfo($"Generating dungeon: {dungeon.Generator.IsGenerating}");
+
+ fakeRoundManager = disabledGameObject.AddComponent();
+ fakeRoundManager.dungeonGenerator = dungeon;
+
+ selectedDungeonFlow.DungeonEvents.onBeforeDungeonGenerate?.Invoke(fakeRoundManager);
+ DungeonManager.GlobalDungeonEvents?.onBeforeDungeonGenerate?.Invoke(fakeRoundManager);
+
+ dungeon.Generate();
+ }
+
+ public void DeleteDungeon(){
+ Plugin.logger.LogInfo($"Deleting dungeon");
+ dungeon.Generator.Clear(true);
+ dungeon.Generator.Cancel();
+
+ dungeon.Generator.RestrictDungeonToBounds = false;
+
+ if (fakeRoundManager) Destroy(fakeRoundManager);
+
+ ClearTransformChildren(dungeon.Root.transform);
+ }
+
+ public void ClearTransformChildren(Transform root){
+ var childCount = root.childCount;
+ for(var i = childCount - 1; i >= 0; --i) {
+ GameObject.Destroy(root.GetChild(i).gameObject);
+ }
+ }
+
+ public void CloseDevDebug(){
+ DeleteDungeon();
+ Destroy(gameObject);
+ }
+
+ public void OnDungeonFinished(DungeonGenerator generator, GenerationStatus status) {
+ var textList = new List();
+ if (status == GenerationStatus.Complete) {
+ textList.Add($"Tiles: {generator.CurrentDungeon.AllTiles.Count}");
+ textList.Add($"Main Tiles: {generator.CurrentDungeon.MainPathTiles.Count}");
+ textList.Add($"Branch Tiles: {generator.CurrentDungeon.BranchPathTiles.Count}");
+ textList.Add($"Doors: {generator.CurrentDungeon.Doors.Count}");
+ } else if (status == GenerationStatus.Failed) {
+ textList.Add("Failed");
+ }
+
+ textList.Add("DunGen");
+ textList.Add(generator.GenerationStats.ToString());
+
+ SetNewSeed();
+ statsTextMesh.text = string.Join("\n", textList);
+ }
+
+ private void SetNewSeed(){
+ foreach(var p in panels) {
+ var mainPanel = p as MainPanel;
+ if (mainPanel) mainPanel.seedInputField.Set(dungeon.Generator.ChosenSeed);
+ }
+ }
+
+ private void UpdatePlusPanel() {
+ foreach(var p in panels) {
+ var plusPanel = p as DunGenPlusPanel;
+ if (plusPanel) plusPanel.UpdatePanel();
+ }
+ }
+
+ public void UpdateDungeonBounds(){
+ foreach(var p in panels) {
+ var plusPanel = p as DunGenPlusPanel;
+ if (plusPanel) plusPanel.UpdateDungeonBoundsHelper();
+ }
+ }
+
+ private void GetAllDungeonFlows(){
+ dungeonFlows = LethalLevelLoader.PatchedContent.ExtendedDungeonFlows.ToArray();
+ dungeonFlowSelectionDropDown.options = dungeonFlows.Select(d => new TMP_Dropdown.OptionData(d.DungeonName)).ToList();
+ SelectDungeonFlow(0);
+ }
+
+ private void CacheMainCamera() {
+ var main = Camera.main;
+ if (main) {
+ lastMainCamera = main;
+ lastCameraPosition = main.transform.position;
+ lastCameraRotation = main.transform.rotation;
+ }
+ }
+
+ private void BeginDevCamera(){
+ lastMainCamera?.gameObject.SetActive(false);
+ devCamera.SetActive(true);
+ }
+
+ private void EndDevCamera(){
+ devCamera.SetActive(false);
+ if (lastMainCamera) {
+ lastMainCamera.transform.position = lastCameraPosition;
+ lastMainCamera.transform.rotation = lastCameraRotation;
+ lastMainCamera.gameObject.SetActive(true);
+ }
+ }
+
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/DevDebugManagerUI.cs b/DunGenPlus/DunGenPlus/DevTools/DevDebugManagerUI.cs
new file mode 100644
index 0000000..bc7faf4
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/DevDebugManagerUI.cs
@@ -0,0 +1,140 @@
+using DunGen;
+using DunGenPlus.Collections;
+using DunGenPlus.DevTools.Panels;
+using DunGenPlus.DevTools.UIElements;
+using LethalLevelLoader;
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+using TMPro;
+using UnityEngine;
+using static System.Collections.Specialized.BitVector32;
+
+namespace DunGenPlus.DevTools {
+
+ internal partial class DevDebugManager : MonoBehaviour {
+
+ [Header("UI Prefabs")]
+ [Header("UI")]
+ public GameObject headerUIPrefab;
+ public GameObject textUIPrefab;
+ public GameObject spaceUIPrefab;
+ public GameObject verticalLayoutUIPrefab;
+
+ [Header("Input Fields")]
+ public GameObject intInputFieldPrefab;
+ public GameObject floatInputFieldPrefab;
+ public GameObject boolInputFieldPrefab;
+ public GameObject stringInputFieldPrefab;
+ public GameObject vector3InputFieldPrefab;
+ public GameObject intSliderFieldPrefab;
+
+ [Header("Special Fields")]
+ public GameObject listUIPrefab;
+ public GameObject optionsUIPrefab;
+
+ public TextUIElement CreateHeaderUIField(Transform parentTransform, string title, float offset) {
+ var gameObject = Instantiate(headerUIPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupBase(title, offset);
+ return field;
+ }
+
+ public TextUIElement CreateTextUIField(Transform parentTransform, string title, float offset) {
+ var gameObject = Instantiate(textUIPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupBase(title, offset);
+ return field;
+ }
+
+ public void CreateSpaceUIField(Transform parentTransform) {
+ Instantiate(spaceUIPrefab, parentTransform);
+ }
+
+ public Transform CreateVerticalLayoutUIField(Transform parentTransform){
+ return Instantiate(verticalLayoutUIPrefab, parentTransform).transform;
+ }
+
+ public IntInputField CreateIntInputField(Transform parentTransform, string title, float offset, int baseValue, Action setAction, int defaultValue = 0){
+ var gameObject = Instantiate(intInputFieldPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+ return field;
+ }
+
+ public FloatInputField CreateFloatInputField(Transform parentTransform, string title, float offset, float baseValue, Action setAction, float defaultValue = 0f){
+ var gameObject = Instantiate(floatInputFieldPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+ return field;
+ }
+
+ public BoolInputField CreateBoolInputField(Transform parentTransform, string title, float offset, bool baseValue, Action setAction){
+ var gameObject = Instantiate(boolInputFieldPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupInputField(title, offset, baseValue, setAction, false);
+ return field;
+ }
+
+ public StringInputField CreateStringInputField(Transform parentTransform, string title, float offset, string baseValue, Action setAction){
+ var gameObject = Instantiate(stringInputFieldPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupInputField(title, offset, baseValue, setAction, string.Empty);
+ return field;
+ }
+
+ public Vector3InputField CreateVector3InputField(Transform parentTransform, string title, float offset, Vector3 baseValue, Action setAction){
+ var gameObject = Instantiate(vector3InputFieldPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupInputField(title, offset, baseValue, setAction, Vector3.zero);
+ return field;
+ }
+
+ public IntSliderField CreateIntSliderField(Transform parentTransform, string title, float offset, int baseValue, Action setAction, int defaultValue = 0){
+ var gameObject = Instantiate(intSliderFieldPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+ return field;
+ }
+
+ public ListUIElement CreateListUIField(Transform parentTransform, string title, float offset, List list){
+ var gameObject = Instantiate(listUIPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupList(title, offset, list);
+ return field;
+ }
+
+
+ public DropdownInputField CreateOptionsUIField(Transform parentTransform, string title, float offset, int baseValue, Action setAction, Func convertIndex, IEnumerable options){
+ var gameObject = Instantiate(optionsUIPrefab, parentTransform);
+ var field = gameObject.GetComponent();
+ field.SetupDropdown(title, offset, baseValue, setAction, convertIndex, options);
+ return field;
+ }
+
+ public DropdownInputField CreateLevelOptionsUIField(Transform parentTransform, string title, float offset, int baseValue, Action setAction){
+ var mainPanel = MainPanel.Instance;
+ return CreateOptionsUIField(parentTransform, title, offset, baseValue, setAction, (i) => mainPanel.levels[i], mainPanel.levelOptions);
+ }
+
+ public DropdownInputField CreateTileOptionsUIField(Transform parentTransform, string title, float offset, int baseValue, Action setAction){
+ var assetCache = DunGenPlusPanel.Instance.selectedAssetCache;
+ return CreateOptionsUIField(parentTransform, title, offset, baseValue, setAction, (i) => assetCache.tiles.list[i].Item, assetCache.tiles.options);
+ }
+
+ public DropdownInputField CreateArchetypeOptionsUIField(Transform parentTransform, string title, float offset, int baseValue, Action setAction){
+ var assetCache = DunGenPlusPanel.Instance.selectedAssetCache;
+ return CreateOptionsUIField(parentTransform, title, offset, baseValue, setAction, (i) => assetCache.archetypes.list[i].Item, assetCache.archetypes.options);
+ }
+
+ public DropdownInputField CreateCopyNodeBehaviourOptionsUIField(Transform parentTransform, string title, float offset, int baseValue, Action setAction){
+ var options = Enum.GetNames(typeof(DunGenExtenderProperties.CopyNodeBehaviour));
+ return CreateOptionsUIField(parentTransform, title, offset, baseValue, setAction, (i) => (DunGenExtenderProperties.CopyNodeBehaviour)i, options);
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/DevDebugOpen.cs b/DunGenPlus/DunGenPlus/DevTools/DevDebugOpen.cs
new file mode 100644
index 0000000..aff4e08
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/DevDebugOpen.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.InputSystem;
+using UnityEngine.InputSystem.Controls;
+
+namespace DunGenPlus.DevTools {
+ internal class DevDebugOpen : MonoBehaviour {
+
+ public static bool IsSinglePlayerInShip(){
+ var startOfRound = StartOfRound.Instance;
+ var roundManager = RoundManager.Instance;
+ if (startOfRound && roundManager) {
+ return startOfRound.connectedPlayersAmount == 0 && startOfRound.inShipPhase;
+ }
+ return false;
+ }
+
+ public void Update(){
+ if (IfKeyPress(Keyboard.current.mKey) && DevDebugManager.Instance == null && IsSinglePlayerInShip()){
+ Instantiate(Assets.DevDebugPrefab);
+ }
+ }
+
+ bool IfKeyPress(params KeyControl[] keys){
+ foreach(var k in keys){
+ if (k.wasPressedThisFrame) return true;
+ }
+ return false;
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/PanelTab.cs b/DunGenPlus/DunGenPlus/DevTools/PanelTab.cs
new file mode 100644
index 0000000..d0f37b5
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/PanelTab.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools {
+ internal class PanelTab : MonoBehaviour {
+ public bool active;
+
+ [Header("References")]
+ public RectTransform rectTransform;
+ public Image image;
+
+ void Update() {
+ var targetHeight = active ? 48f : 36f;
+ var targetColor = active ? new Color(100f / 255f, 100f / 255f, 100f / 255f, 1f) : new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);
+
+ var size = rectTransform.sizeDelta;
+ size.y = Mathf.Lerp(size.y, targetHeight, Time.deltaTime * 10f);
+ rectTransform.sizeDelta = size;
+
+ var color = image.color;
+ color = Color.Lerp(color, targetColor, Time.deltaTime * 10f);
+ image.color = color;
+ }
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/Panels/BasePanel.cs b/DunGenPlus/DunGenPlus/DevTools/Panels/BasePanel.cs
new file mode 100644
index 0000000..e1c4dc0
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/Panels/BasePanel.cs
@@ -0,0 +1,53 @@
+using DunGen;
+using DunGen.Graph;
+using LethalLevelLoader;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Remoting.Messaging;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace DunGenPlus.DevTools.Panels {
+ internal abstract class BasePanel : MonoBehaviour {
+
+ public DevDebugManager manager => DevDebugManager.Instance;
+ public RuntimeDungeon dungeon => manager.dungeon;
+ public ExtendedDungeonFlow selectedExtendedDungeonFlow => manager.selectedDungeonFlow;
+ public DungeonFlow selectedDungeonFlow => selectedExtendedDungeonFlow.DungeonFlow;
+
+ [Header("Renders")]
+ public GameObject mainGameObject;
+ public PanelTab tab;
+
+ public virtual void AwakeCall() {
+
+ }
+
+ public virtual void SetPanelVisibility(bool visible) {
+ mainGameObject.SetActive(visible);
+ tab.active = visible;
+ }
+
+ protected int ParseTextInt(string text, int defaultValue = 0) {
+ if (int.TryParse(text, out var result)){
+ return result;
+ } else {
+ Plugin.logger.LogWarning($"Couldn't parse {text} into an int");
+ return defaultValue;
+ }
+ }
+
+ protected float ParseTextFloat(string text, float defaultValue = 0f) {
+ if (float.TryParse(text, out var result)){
+ return result;
+ } else {
+ Plugin.logger.LogWarning($"Couldn't parse {text} into a float");
+ return defaultValue;
+ }
+ }
+
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/Panels/DunGenPlusPanel.cs b/DunGenPlus/DunGenPlus/DevTools/Panels/DunGenPlusPanel.cs
new file mode 100644
index 0000000..3918924
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/Panels/DunGenPlusPanel.cs
@@ -0,0 +1,258 @@
+using DunGen;
+using DunGen.Graph;
+using DunGenPlus.Collections;
+using LethalLevelLoader;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine;
+using UnityEngine.UI;
+using DunGenPlus.DevTools.UIElements;
+
+namespace DunGenPlus.DevTools.Panels {
+ internal class DunGenPlusPanel : BasePanel {
+
+ public static DunGenPlusPanel Instance { get; private set; }
+
+ internal DungeonFlow previousDungeonFlow;
+ internal DunGenExtender selectedExtenderer;
+ internal DungeonFlowCacheAssets selectedAssetCache;
+
+ [Header("Panel References")]
+ public GameObject createGameObject;
+ public GameObject selectedGameObject;
+
+ [Header("Dungeon Bounds Helper")]
+ public GameObject dungeonBoundsHelperGameObject;
+
+ [Header("Selected Panel References")]
+ public Toggle activateDunGenPlusToggle;
+
+ private GameObject mainPathParentGameobject;
+ private GameObject dungeonBoundsParentGameobject;
+ private GameObject archetypesNodesParentGameobject;
+
+ public class DungeonFlowCacheAssets {
+ public DunGenExtenderProperties originalProperties;
+
+ public struct Collection {
+ public List list;
+ public Dictionary dictionary;
+ public IEnumerable options;
+
+ public Collection(List list) {
+ this.list = list;
+
+ dictionary = new Dictionary();
+ for(var i = 0; i < list.Count; i++) {
+ dictionary.Add(list[i], i);
+ }
+
+ options = list.Select(l => l.ToString());
+ }
+ }
+
+ public Collection> tileSets;
+ public Collection> tiles;
+ public Collection> archetypes;
+
+ public DungeonFlowCacheAssets(DunGenExtender extender){
+ originalProperties = extender.Properties.Copy();
+
+ var tileSetsHashSet = new HashSet>() { new NullObject(null) };
+ var tilesHashSet = new HashSet>() { new NullObject(null) };
+ var archetypesHashSet = new HashSet>() { new NullObject(null) };
+
+ foreach(var t in extender.DungeonFlow.Nodes) {
+ var label = t.Label.ToLowerInvariant();
+ if (label == "lchc gate" || label == "goal"){
+ foreach(var n in t.TileSets.SelectMany(x => x.TileWeights.Weights)) {
+ n.Value.GetComponent().RepeatMode = TileRepeatMode.Allow;
+ }
+ }
+
+ }
+
+ foreach(var t in extender.DungeonFlow.Nodes.SelectMany(n => n.TileSets)) {
+ tileSetsHashSet.Add(t);
+ foreach(var x in t.TileWeights.Weights) {
+ tilesHashSet.Add(x.Value);
+ }
+ }
+ foreach(var a in extender.DungeonFlow.Lines.SelectMany(l => l.DungeonArchetypes)) {
+ archetypesHashSet.Add(a);
+ foreach(var t in a.TileSets) {
+ tileSetsHashSet.Add(t);
+ foreach(var x in t.TileWeights.Weights) {
+ tilesHashSet.Add(x.Value);
+ }
+ }
+ }
+
+ foreach(var n in extender.Properties.NormalNodeArchetypes) {
+ foreach(var a in n.archetypes){
+ archetypesHashSet.Add(a);
+ }
+ }
+
+ tileSets = new Collection>(tileSetsHashSet.ToList());
+ tiles = new Collection>(tilesHashSet.ToList());
+ archetypes = new Collection>(archetypesHashSet.ToList());
+ }
+ }
+
+ public Dictionary cacheDictionary = new Dictionary();
+
+ public override void AwakeCall() {
+ Instance = this;
+
+ dungeonBoundsHelperGameObject.SetActive(false);
+ }
+
+ public override void SetPanelVisibility(bool visible) {
+ base.SetPanelVisibility(visible);
+
+ if (visible) UpdatePanel();
+ }
+
+ public void CreateDunGenPlusExtenderer(){
+ var asset = API.CreateDunGenExtender(selectedDungeonFlow);
+ selectedDungeonFlow.TileInjectionRules = new List();
+ API.AddDunGenExtender(asset);
+ SetPanelVisibility(true);
+ UpdatePanel();
+ }
+
+ public void UpdatePanel(){
+ if (previousDungeonFlow == selectedDungeonFlow) return;
+
+ var hasAsset = API.ContainsDungeonFlow(selectedDungeonFlow);
+ selectedGameObject.SetActive(hasAsset);
+ createGameObject.SetActive(!hasAsset);
+
+ ClearPanel();
+ if (hasAsset) {
+ SetupPanel();
+ } else {
+ previousDungeonFlow = null;
+ selectedExtenderer = null;
+ selectedAssetCache = null;
+ dungeonBoundsHelperGameObject.SetActive(false);
+ }
+ }
+
+ public void SetupPanel() {
+ var dungeonFlow = selectedDungeonFlow;
+ var extender = API.GetDunGenExtender(dungeonFlow);
+ if (!cacheDictionary.TryGetValue(dungeonFlow, out var cache)) {
+ cache = new DungeonFlowCacheAssets(extender);
+ cacheDictionary.Add(dungeonFlow, cache);
+ }
+
+ previousDungeonFlow = dungeonFlow;
+ selectedExtenderer = extender;
+ selectedAssetCache = cache;
+
+ var parentTransform = selectedGameObject.transform;
+ var properties = selectedExtenderer.Properties;
+ manager.CreateBoolInputField(parentTransform, "Activate DunGenPlus", 0f, selectedExtenderer.Active, SetActivateDunGenPlus);
+ manager.CreateSpaceUIField(parentTransform);
+
+ var mainPathTransform = manager.CreateVerticalLayoutUIField(parentTransform);
+ mainPathParentGameobject = mainPathTransform.gameObject;
+ manager.CreateHeaderUIField(parentTransform, "Main Path", 0f);
+ manager.CreateIntSliderField(parentTransform, "Main Path Count", 0f, properties.MainPathCount, SetMainPathCount);
+ mainPathTransform.SetAsLastSibling();
+ manager.CreateTileOptionsUIField(mainPathTransform, "Main Room Tile Prefab", 0f, selectedAssetCache.tiles.dictionary[properties.MainRoomTilePrefab], SetMainRoom);
+ manager.CreateCopyNodeBehaviourOptionsUIField(mainPathTransform, "Copy Node Behaviour", 0f, (int)properties.MainPathCopyNodeBehaviour, SetCopyNodeBehaviour);
+ manager.CreateSpaceUIField(parentTransform);
+
+ var dungeonBoundsTransform = manager.CreateVerticalLayoutUIField(parentTransform);
+ dungeonBoundsParentGameobject = dungeonBoundsTransform.gameObject;
+ manager.CreateHeaderUIField(parentTransform, "Dungeon Bounds", 0f);
+ manager.CreateBoolInputField(parentTransform, "Use Dungeon Bounds", 0f, properties.UseDungeonBounds, SetUseDungeonBounds);
+ dungeonBoundsTransform.SetAsLastSibling();
+ manager.CreateVector3InputField(dungeonBoundsTransform, "Size Base", 0f, properties.DungeonSizeBase, SetDungeonBoundsSizeBase);
+ manager.CreateVector3InputField(dungeonBoundsTransform, "Size Factor", 0f, properties.DungeonSizeFactor, SetDungeonBoundsSizeFactor);
+ manager.CreateVector3InputField(dungeonBoundsTransform, "Position Offset", 0f, properties.DungeonPositionOffset, SetDungeonBoundsPosOffset);
+ manager.CreateVector3InputField(dungeonBoundsTransform, "Position Pivot", 0f, properties.DungeonPositionPivot, SetDungeonBoundsPosPivot);
+ manager.CreateSpaceUIField(parentTransform);
+
+ var archetypesTransform = manager.CreateVerticalLayoutUIField(parentTransform);
+ archetypesNodesParentGameobject = archetypesTransform.gameObject;
+ manager.CreateHeaderUIField(parentTransform, "Archetypes Normal Nodes", 0f);
+ manager.CreateBoolInputField(parentTransform, "Add Archetypes", 0f, properties.AddArchetypesToNormalNodes, SetAddArchetypes);
+ archetypesTransform.SetAsLastSibling();
+ manager.CreateListUIField(archetypesTransform, "Normal Node Archetypes", 0f, properties.NormalNodeArchetypes);
+ manager.CreateSpaceUIField(parentTransform);
+
+ dungeonBoundsHelperGameObject.SetActive(selectedExtenderer.Properties.UseDungeonBounds);
+ UpdateDungeonBoundsHelper();
+ }
+
+ public void ClearPanel(){
+ manager.ClearTransformChildren(selectedGameObject.transform);
+ }
+
+ public void SetActivateDunGenPlus(bool state){
+ selectedExtenderer.Active = state;
+ }
+
+ public void SetMainPathCount(int value) {
+ selectedExtenderer.Properties.MainPathCount = value;
+ mainPathParentGameobject.SetActive(value > 1);
+ }
+
+ public void SetMainRoom(GameObject value) {
+ selectedExtenderer.Properties.MainRoomTilePrefab = value;
+ }
+
+ public void SetCopyNodeBehaviour(DunGenExtenderProperties.CopyNodeBehaviour value) {
+ selectedExtenderer.Properties.MainPathCopyNodeBehaviour = value;
+ }
+
+ public void SetUseDungeonBounds(bool state){
+ selectedExtenderer.Properties.UseDungeonBounds = state;
+ dungeonBoundsHelperGameObject.SetActive(state);
+ dungeonBoundsParentGameobject.SetActive(state);
+ }
+
+ public void UpdateDungeonBoundsHelper(){
+ if (selectedExtenderer == null) return;
+
+ var t = dungeonBoundsHelperGameObject.transform;
+ var result = selectedExtenderer.Properties.GetDungeonBounds(dungeon.Generator.LengthMultiplier);
+ t.localPosition = result.center;
+ t.localScale = result.size;
+ }
+
+ public void SetDungeonBoundsSizeBase(Vector3 value) {
+ selectedExtenderer.Properties.DungeonSizeBase = value;
+ UpdateDungeonBoundsHelper();
+ }
+
+ public void SetDungeonBoundsSizeFactor(Vector3 value) {
+ selectedExtenderer.Properties.DungeonSizeFactor = value;
+ UpdateDungeonBoundsHelper();
+ }
+
+ public void SetDungeonBoundsPosOffset(Vector3 value) {
+ selectedExtenderer.Properties.DungeonPositionOffset = value;
+ UpdateDungeonBoundsHelper();
+ }
+
+ public void SetDungeonBoundsPosPivot(Vector3 value) {
+ selectedExtenderer.Properties.DungeonPositionPivot = value;
+ UpdateDungeonBoundsHelper();
+ }
+
+ public void SetAddArchetypes(bool state){
+ selectedExtenderer.Properties.AddArchetypesToNormalNodes = state;
+ archetypesNodesParentGameobject.SetActive(state);
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/Panels/MainPanel.cs b/DunGenPlus/DunGenPlus/DevTools/Panels/MainPanel.cs
new file mode 100644
index 0000000..fa9731c
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/Panels/MainPanel.cs
@@ -0,0 +1,98 @@
+using DunGen;
+using DunGenPlus.DevTools.UIElements;
+using LethalLevelLoader;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools.Panels {
+ internal class MainPanel : BasePanel {
+
+ public static MainPanel Instance { get; private set; }
+
+ internal IntInputField seedInputField;
+ internal TextUIElement lengthMultiplierField;
+
+ internal ExtendedLevel[] levels;
+ internal IEnumerable levelOptions;
+
+ public override void AwakeCall(){
+ Instance = this;
+
+ GetAllLevels();
+
+ var gen = dungeon.Generator;
+ var parentTransform = mainGameObject.transform;
+
+ manager.CreateHeaderUIField(parentTransform, "Dungeon Generator", 0f);
+ seedInputField = manager.CreateIntInputField(parentTransform, "Seed", 0f, gen.Seed, SetSeed);
+ manager.CreateBoolInputField(parentTransform, "Randomize Seed", 0f, gen.ShouldRandomizeSeed, SetRandomSeed);
+ manager.CreateSpaceUIField(parentTransform);
+
+ manager.CreateIntInputField(parentTransform, "Max Attempts", 0f, gen.MaxAttemptCount, SetMaxAttempts, 10);
+ manager.CreateSpaceUIField(parentTransform);
+
+ manager.CreateBoolInputField(parentTransform, "Generate Async", 0f, gen.GenerateAsynchronously, SetGenerateAsync);
+ manager.CreateFloatInputField(parentTransform, "Max Async (ms)", 0f, gen.MaxAsyncFrameMilliseconds, SetMaxAsync);
+ manager.CreateFloatInputField(parentTransform, "Pause Betwoon Rooms", 0f, gen.PauseBetweenRooms, SetPauseBetweenRooms);
+ manager.CreateSpaceUIField(parentTransform);
+
+ manager.CreateHeaderUIField(parentTransform, "Levels", 0f);
+ manager.CreateLevelOptionsUIField(parentTransform, "Level", 0f, 0, SetLevel);
+ lengthMultiplierField = manager.CreateTextUIField(parentTransform, "Length Multiplier", 0f);
+ SetLevel(levels[0]);
+ }
+
+ public void SetSeed(int value) {
+ dungeon.Generator.Seed = value;
+ }
+
+ public void SetRandomSeed(bool state) {
+ dungeon.Generator.ShouldRandomizeSeed = state;
+ }
+
+ public void SetMaxAttempts(int value) {
+ dungeon.Generator.MaxAttemptCount = value;
+ }
+
+ public void SetGenerateAsync(bool state) {
+ dungeon.Generator.GenerateAsynchronously = state;
+ }
+
+ public void SetMaxAsync(float value) {
+ dungeon.Generator.MaxAsyncFrameMilliseconds = value;
+ }
+
+ public void SetPauseBetweenRooms(float value) {
+ dungeon.Generator.PauseBetweenRooms = value;
+ }
+
+ private void GetAllLevels(){
+ levels = LethalLevelLoader.PatchedContent.ExtendedLevels.ToArray();
+ levelOptions = levels.Select(l => l.NumberlessPlanetName);
+ }
+
+ public void SetLevel(ExtendedLevel level){
+ var currentLevelLengthMultlpier = GetLevelMultiplier(level);
+ dungeon.Generator.LengthMultiplier = currentLevelLengthMultlpier;
+ manager.UpdateDungeonBounds();
+ lengthMultiplierField.SetText($"Length multiplier: {currentLevelLengthMultlpier.ToString("F2")}");
+ }
+
+ private float GetLevelMultiplier(ExtendedLevel level){
+ var roundManager = RoundManager.Instance;
+ if (roundManager == null) {
+ Plugin.logger.LogError("RoundManager somehow null. Can't set level length multiplier");
+ return 1f;
+ }
+
+ return roundManager.mapSizeMultiplier * level.SelectableLevel.factorySizeMultiplier;
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/BaseInputField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/BaseInputField.cs
new file mode 100644
index 0000000..3dde0b4
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/BaseInputField.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools.UIElements {
+ internal abstract class BaseInputField : BaseUIElement {
+
+ public virtual void SetupInputField(string titleText, float offset, T baseValue, Action setAction, T defaultValue){
+ SetupBase(titleText, offset);
+ }
+
+ public abstract void Set(T value);
+
+ protected int ParseTextInt(string text, int defaultValue = 0) {
+ if (int.TryParse(text, out var result)){
+ return result;
+ } else {
+ Plugin.logger.LogWarning($"Couldn't parse {text} into an int");
+ return defaultValue;
+ }
+ }
+
+ protected float ParseTextFloat(string text, float defaultValue = 0f) {
+ if (float.TryParse(text, out var result)){
+ return result;
+ } else {
+ Plugin.logger.LogWarning($"Couldn't parse {text} into a float");
+ return defaultValue;
+ }
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/BaseUIElement.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/BaseUIElement.cs
new file mode 100644
index 0000000..ed128fb
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/BaseUIElement.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools.UIElements {
+ internal abstract class BaseUIElement : MonoBehaviour {
+
+ public TextMeshProUGUI titleTextMesh;
+ internal string title;
+
+ public LayoutElement layoutElement;
+ internal float layoutOffset;
+
+ public void SetupBase(string titleText, float offset) {
+ title = titleText;
+ SetText(title);
+
+ layoutOffset = offset;
+ if (layoutElement) {
+ layoutElement.minWidth -= layoutOffset;
+ }
+
+ }
+
+ public void SetText(string value) {
+ titleTextMesh.text = value;
+ }
+
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/BoolInputField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/BoolInputField.cs
new file mode 100644
index 0000000..4b877fa
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/BoolInputField.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools.UIElements {
+ internal class BoolInputField : BaseInputField {
+
+ public Toggle toggle;
+
+ public override void SetupInputField(string title, float offset, bool baseValue, Action setAction, bool defaultValue) {
+ base.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+
+ toggle.onValueChanged.AddListener((t) => SetValue(setAction, t));
+ Set(baseValue);
+ }
+
+ private void SetValue(Action setAction, bool state) {
+ Plugin.logger.LogInfo($"Setting {title} to {state}");
+ setAction.Invoke(state);
+ }
+
+ public override void Set(bool state){
+ toggle.isOn = state;
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/DropdownInputField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/DropdownInputField.cs
new file mode 100644
index 0000000..3b3557c
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/DropdownInputField.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools.UIElements
+{
+ internal class DropdownInputField : BaseUIElement {
+
+ public TMP_Dropdown dropDown;
+
+ public void SetupDropdown(string titleText, float offset, int baseValue, Action setAction, Func convertIndex, IEnumerable options) {
+ SetupBase(titleText, offset);
+
+ dropDown.options = options.Select(c => {
+ return new TMP_Dropdown.OptionData(c.Substring(0, Math.Min(24, c.Length)));
+ }).ToList();
+
+ dropDown.onValueChanged.AddListener((t) => SetValue(setAction, convertIndex, t));
+ dropDown.value = baseValue;
+ }
+
+ private void SetValue(Action setAction, Func convertIndex, int index) {
+ var value = convertIndex.Invoke(index);
+ Plugin.logger.LogInfo($"Setting {title} to {value}");
+ setAction.Invoke(value);
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/FloatInputField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/FloatInputField.cs
new file mode 100644
index 0000000..77c88a4
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/FloatInputField.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+
+namespace DunGenPlus.DevTools.UIElements
+{
+ internal class FloatInputField : BaseInputField {
+
+ public TMP_InputField inputField;
+ internal float defaultValue = 0f;
+
+ public override void SetupInputField(string title, float offset, float baseValue, Action setAction , float defaultValue) {
+ base.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+ this.defaultValue = defaultValue;
+
+ inputField.onValueChanged.AddListener((t) => SetValue(setAction, t));
+ Set(baseValue);
+ }
+
+ private void SetValue(Action setAction, string text) {
+ Plugin.logger.LogInfo($"Setting {title} to {text}");
+ setAction.Invoke(ParseTextFloat(text, defaultValue));
+ }
+
+ public override void Set(float value){
+ inputField.text = value.ToString();
+ }
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/IntInputField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/IntInputField.cs
new file mode 100644
index 0000000..ab0dec3
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/IntInputField.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+
+namespace DunGenPlus.DevTools.UIElements {
+ internal class IntInputField : BaseInputField {
+
+ public TMP_InputField inputField;
+ internal int defaultValue = 0;
+
+ public override void SetupInputField(string title, float offset, int baseValue, Action setAction , int defaultValue) {
+ base.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+ this.defaultValue = defaultValue;
+
+ inputField.onValueChanged.AddListener((t) => SetValue(setAction, t));
+ Set(baseValue);
+ }
+
+ private void SetValue(Action setAction, string text) {
+ Plugin.logger.LogInfo($"Setting {title} to {text}");
+ setAction.Invoke(ParseTextInt(text, defaultValue));
+ }
+
+ public override void Set(int value){
+ inputField.text = value.ToString();
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/IntSliderField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/IntSliderField.cs
new file mode 100644
index 0000000..83e866b
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/IntSliderField.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools.UIElements {
+
+ internal class IntSliderField : BaseInputField {
+
+ public Slider inputField;
+ public TextMeshProUGUI textMesh;
+ internal int defaultValue = 0;
+
+ public override void SetupInputField(string title, float offset, int baseValue, Action setAction , int defaultValue) {
+ base.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+ this.defaultValue = defaultValue;
+
+ inputField.onValueChanged.AddListener((t) => SetValue(setAction, t));
+ Set(baseValue);
+ }
+
+ private void SetValue(Action setAction, float value) {
+ Plugin.logger.LogInfo($"Setting {title} to {value}");
+ setAction.Invoke((int)value);
+ }
+
+ public override void Set(int value){
+ inputField.value = value;
+ textMesh.text = value.ToString();
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/ListUIElement.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/ListUIElement.cs
new file mode 100644
index 0000000..1725d78
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/ListUIElement.cs
@@ -0,0 +1,77 @@
+using DunGen;
+using DunGenPlus.Collections;
+using DunGenPlus.DevTools.Panels;
+using LethalLevelLoader;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine;
+
+namespace DunGenPlus.DevTools.UIElements {
+ internal class ListUIElement : BaseUIElement {
+
+ public GameObject templatePrefab;
+ public Transform listTransform;
+
+ internal IList list;
+ internal Type listType;
+
+ public void SetupList(string titleText, float offset, List list) {
+ SetupBase(titleText, offset);
+
+ this.list = list;
+ listType = typeof(T);
+ for(var i = 0; i < list.Count; ++i) {
+ CreateEntry(i);
+ }
+ }
+
+ public void AddElement() {
+ object item = null;
+ if (listType == typeof(DungeonArchetype)) {
+ item = null;
+ } else if (listType == typeof(NodeArchetype)) {
+ item = new NodeArchetype();
+ }
+
+ list.Add(item);
+ CreateEntry(list.Count - 1);
+ }
+
+ public void RemoveElement(){
+ if (list.Count == 0) return;
+ list.RemoveAt(list.Count - 1);
+ Destroy(listTransform.GetChild(listTransform.childCount - 1).gameObject);
+ }
+
+ public void CreateEntry(int index){
+ var copy = CreateCopy(index);
+ var copyParentTransform = copy.transform.Find("Items");
+
+ if (listType == typeof(DungeonArchetype)){
+ var entry = (DungeonArchetype)list[index];
+ var baseValue = DunGenPlusPanel.Instance.selectedAssetCache.archetypes.dictionary[entry];
+ DevDebugManager.Instance.CreateArchetypeOptionsUIField(copyParentTransform, "Archetype", layoutOffset + 24f, baseValue, (t) => list[index] = t);
+ }
+
+ else if (listType == typeof(NodeArchetype)) {
+ var entry = (NodeArchetype)list[index];
+ DevDebugManager.Instance.CreateStringInputField(copyParentTransform, "Label", layoutOffset + 24f, entry.label, (t) => entry.label = t);
+ DevDebugManager.Instance.CreateListUIField(copyParentTransform, "Archetypes", layoutOffset + 24f, entry.archetypes);
+ }
+
+ copy.SetActive(true);
+ }
+
+ public GameObject CreateCopy(int index){
+ var copy = Instantiate(templatePrefab, listTransform);
+ copy.transform.Find("Element").GetComponent().text = $"Element {index}";
+ return copy;
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/StringInputField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/StringInputField.cs
new file mode 100644
index 0000000..340ecd0
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/StringInputField.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+
+namespace DunGenPlus.DevTools.UIElements
+{
+ internal class StringInputField : BaseInputField {
+
+ public TMP_InputField inputField;
+
+ public override void SetupInputField(string title, float offset, string baseValue, Action setAction, string defaultValue) {
+ base.SetupInputField(title, offset, baseValue, setAction, defaultValue);
+
+ inputField.onValueChanged.AddListener((t) => SetValue(setAction, t));
+ Set(baseValue);
+ }
+
+ private void SetValue(Action setAction, string text) {
+ Plugin.logger.LogInfo($"Setting {title} to {text}");
+ setAction.Invoke(text);
+ }
+
+ public override void Set(string value){
+ inputField.text = value;
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/TextUIElement.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/TextUIElement.cs
new file mode 100644
index 0000000..64309bd
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/TextUIElement.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DunGenPlus.DevTools.UIElements {
+ internal class TextUIElement : BaseUIElement {
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DevTools/UIElements/Vector3InputField.cs b/DunGenPlus/DunGenPlus/DevTools/UIElements/Vector3InputField.cs
new file mode 100644
index 0000000..b4ad5b0
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/DevTools/UIElements/Vector3InputField.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TMPro;
+using UnityEngine;
+using UnityEngine.Events;
+using UnityEngine.UI;
+
+namespace DunGenPlus.DevTools.UIElements {
+
+ internal class Vector3InputField : BaseInputField {
+
+ public TMP_InputField xInputField;
+ public TMP_InputField yInputField;
+ public TMP_InputField zInputField;
+ private Vector3 _value;
+
+ public override void SetupInputField(string titleText, float offset, Vector3 baseValue, Action setAction, Vector3 defaultValue) {
+ base.SetupInputField(titleText, offset, baseValue, setAction, defaultValue);
+
+ xInputField.onValueChanged.AddListener((t) => SetXValue(setAction, t));
+ yInputField.onValueChanged.AddListener((t) => SetYValue(setAction, t));
+ zInputField.onValueChanged.AddListener((t) => SetZValue(setAction, t));
+
+ Set(baseValue);
+ }
+
+ private void SetXValue(Action setAction, string text){
+ Plugin.logger.LogInfo($"Setting {title}.x to {text}");
+ _value.x = ParseTextFloat(text);
+ setAction.Invoke(_value);
+ }
+
+ private void SetYValue(Action setAction, string text){
+ Plugin.logger.LogInfo($"Setting {title}.y to {text}");
+ _value.y = ParseTextFloat(text);
+ setAction.Invoke(_value);
+ }
+
+ private void SetZValue(Action setAction, string text){
+ Plugin.logger.LogInfo($"Setting {title}.z to {text}");
+ _value.z = ParseTextFloat(text);
+ setAction.Invoke(_value);
+ }
+
+ public override void Set(Vector3 value){
+ _value = value;
+ xInputField.text = value.x.ToString();
+ yInputField.text = value.y.ToString();
+ zInputField.text = value.z.ToString();
+ }
+
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/DunGenExtender.cs b/DunGenPlus/DunGenPlus/DunGenExtender.cs
index a352dec..973742d 100644
--- a/DunGenPlus/DunGenPlus/DunGenExtender.cs
+++ b/DunGenPlus/DunGenPlus/DunGenExtender.cs
@@ -20,6 +20,7 @@ namespace DunGenPlus {
[Header("DEV ONLY: DON'T TOUCH")]
public string Version = "0";
+ internal bool Active = true;
}
}
diff --git a/DunGenPlus/DunGenPlus/DunGenPlus.csproj b/DunGenPlus/DunGenPlus/DunGenPlus.csproj
index cd3d154..8689e66 100644
--- a/DunGenPlus/DunGenPlus/DunGenPlus.csproj
+++ b/DunGenPlus/DunGenPlus/DunGenPlus.csproj
@@ -46,8 +46,8 @@
..\..\..\Libraries\BepInEx.Harmony.dll
-
- ..\..\..\Libraries\LethalLevelLoader.dll
+
+ ..\..\..\Libraries\LethalLevelLoader-publicized.dll
@@ -60,6 +60,10 @@
..\..\..\Libraries\Unity.Collections.dll
+
+ False
+ ..\..\..\Libraries\Unity.InputSystem.dll
+
False
..\..\..\Libraries\Unity.Netcode.Components.dll
@@ -80,6 +84,10 @@
False
..\..\..\Libraries\Unity.RenderPipelines.HighDefinition.Runtime.dll
+
+ False
+ ..\..\..\Libraries\Unity.TextMeshPro.dll
+
..\..\..\Libraries\UnityEngine.dll
@@ -90,6 +98,33 @@
..\..\..\Libraries\UnityEngine.CoreModule.dll
+
+ False
+ ..\..\..\Libraries\UnityEngine.IMGUIModule.dll
+
+
+ False
+ ..\..\..\Libraries\UnityEngine.InputLegacyModule.dll
+
+
+ False
+ ..\..\..\Libraries\UnityEngine.InputModule.dll
+
+
+ False
+ ..\..\..\Libraries\UnityEngine.UI.dll
+
+
+ False
+ ..\..\..\Libraries\UnityEngine.UIElementsModule.dll
+
+
+ ..\..\..\Libraries\UnityEngine.UIElementsNativeModule.dll
+
+
+ False
+ ..\..\..\Libraries\UnityEngine.UIModule.dll
+
@@ -98,6 +133,7 @@
+
@@ -109,6 +145,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -116,6 +170,7 @@
+
@@ -124,8 +179,17 @@
+
+
+
+
+
+
copy "$(TargetPath)" "C:\Users\Jose Garcia\AppData\Roaming\r2modmanPlus-local\LethalCompany\profiles\SDM Debug\BepInEx\plugins\Alice-DungeonGenerationPlus\$(TargetName).dll"
+
+ copy "D:\Previous Computer\Desktop\LethalCompany Modding\Unity Template\Assets\AssetBundles\dungen" "$(ProjectDir)\dungen"
+
\ No newline at end of file
diff --git a/DunGenPlus/DunGenPlus/Patches/LethalLevelLoaderPatches.cs b/DunGenPlus/DunGenPlus/Patches/LethalLevelLoaderPatches.cs
new file mode 100644
index 0000000..1753734
--- /dev/null
+++ b/DunGenPlus/DunGenPlus/Patches/LethalLevelLoaderPatches.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using DunGenPlus.DevTools;
+using HarmonyLib;
+
+
+namespace DunGenPlus.Patches {
+ internal class LethalLevelLoaderPatches {
+
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(LethalLevelLoader.Patches), "DungeonGeneratorGenerate_Prefix")]
+ public static bool DungeonGeneratorGenerate_Prefix_Prefix(){
+ return DevDebugManager.Instance == null;
+ }
+
+ }
+}
diff --git a/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs b/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs
index 1412850..43a404c 100644
--- a/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs
+++ b/DunGenPlus/DunGenPlus/Patches/RoundManagerPatch.cs
@@ -1,5 +1,6 @@
using DunGen;
using DunGenPlus.Components.Scrap;
+using DunGenPlus.DevTools;
using DunGenPlus.Generation;
using HarmonyLib;
using System;
@@ -9,10 +10,17 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
+using UnityEngine;
namespace DunGenPlus.Patches {
public class RoundManagerPatch {
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(RoundManager), "Awake")]
+ public static void AwakePatch(){
+ var devDebug = new GameObject("DevDebugOpen", typeof(DevDebugOpen));
+ }
+
[HarmonyPrefix]
[HarmonyPatch(typeof(RoundManager), "waitForScrapToSpawnToSync")]
public static void waitForScrapToSpawnToSyncPatch (ref RoundManager __instance, ref NetworkObjectReference[] spawnedScrap, ref int[] scrapValues) {
diff --git a/DunGenPlus/DunGenPlus/Plugin.cs b/DunGenPlus/DunGenPlus/Plugin.cs
index 62aa3ea..7bfba65 100644
--- a/DunGenPlus/DunGenPlus/Plugin.cs
+++ b/DunGenPlus/DunGenPlus/Plugin.cs
@@ -19,6 +19,7 @@ using UnityEngine.Assertions;
namespace DunGenPlus {
[BepInPlugin(modGUID, modName, modVersion)]
+ [BepInDependency("imabatby.lethallevelloader", "1.2.0.3")]
[BepInProcess("Lethal Company.exe")]
public class Plugin : BaseUnityPlugin {
@@ -44,9 +45,17 @@ namespace DunGenPlus {
Harmony.PatchAll(typeof(DoorwayConnectionPatch));
Harmony.PatchAll(typeof(RoundManagerPatch));
+ try {
+ Harmony.PatchAll(typeof(LethalLevelLoaderPatches));
+ } catch (Exception e) {
+ Plugin.logger.LogError("Failed to patch LLL for dev debug. You can ignore this.");
+ Plugin.logger.LogError(e);
+ }
+
//Harmony.PatchAll(typeof(StartOfRoundPatch));
Assets.LoadAssets();
+ Assets.LoadAssetBundle();
DungeonManager.GlobalDungeonEvents.onBeforeDungeonGenerate.AddListener(OnDunGenExtenderLoad);
DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction);
}
@@ -56,9 +65,10 @@ namespace DunGenPlus {
var generator = roundManager.dungeonGenerator.Generator;
var flow = generator.DungeonFlow;
- if (DunGenExtenders.TryGetValue(flow, out var value)) {
+ var extender = API.GetDunGenExtender(flow);
+ if (extender && extender.Active) {
Plugin.logger.LogInfo($"Loading DunGenExtender for {flow.name}");
- DunGenPlusGenerator.Activate(generator, value);
+ DunGenPlusGenerator.Activate(generator, extender);
return;
}
diff --git a/DunGenPlus/DunGenPlus/dungen b/DunGenPlus/DunGenPlus/dungen
new file mode 100644
index 0000000..090273d
Binary files /dev/null and b/DunGenPlus/DunGenPlus/dungen differ