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