using System; using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using Sirenix.Utilities; using SLSUtilities.General; using UnityEngine; using UnityEngine.Serialization; #if UNITY_EDITOR using UnityEditor; using Sirenix.OdinInspector.Editor; using Sirenix.Utilities.Editor; #endif namespace Cielonos.MainGame.Map { [CreateAssetMenu(fileName = "ZoneData", menuName = "Cielonos/MainGame/Map/ZoneData", order = 1)] public partial class ZoneData : SerializedScriptableObject { [InlineProperty] public struct SpawnPointKey : System.IEquatable { [HorizontalGroup("Row")] [LabelText("Spawn Point")] [LabelWidth(90)] public string group; [HorizontalGroup("Row", Width = 50, MarginLeft = 10)] [HideLabel] public int index; public SpawnPointKey(string group, int index) { this.group = group; this.index = index; } public bool Equals(SpawnPointKey other) { return group == other.group && index == other.index; } public override bool Equals(object obj) { return obj is SpawnPointKey other && Equals(other); } public override int GetHashCode() { unchecked { return ((group != null ? group.GetHashCode() : 0) * 397) ^ index; } } public static bool operator ==(SpawnPointKey left, SpawnPointKey right) { return left.Equals(right); } public static bool operator !=(SpawnPointKey left, SpawnPointKey right) { return !left.Equals(right); } public override string ToString() => $"{group}_{index}"; } [System.Serializable] [InlineProperty] [HideReferenceObjectPicker] public class EnemySpawnMapping { [HideInInspector] [System.NonSerialized] public ZoneData parent; [FormerlySerializedAs("Group")] [HorizontalGroup("SpawnInfo", 0.25f), HideLabel] [ReadOnly] public string group; [FormerlySerializedAs("Index")] [HorizontalGroup("SpawnInfo", 0.15f), HideLabel] [ReadOnly] public int index; [FormerlySerializedAs("EnemyID")] [HorizontalGroup("SpawnInfo", 0.5f), HideLabel] [ValueDropdown("GetEnemyKeys")] [OnValueChanged("OnEnemyIDChanged")] public string enemyID; public EnemySpawnMapping() { } public EnemySpawnMapping(SpawnPointKey key, string val, ZoneData parent) { group = key.group; index = key.index; enemyID = val; this.parent = parent; } #if UNITY_EDITOR private void OnEnemyIDChanged() { if (parent != null) { parent.UpdateDictionaryEntry(group, index, enemyID); } } #endif private IEnumerable GetEnemyKeys() { return MapBaseCollection.Enemies.Keys; } } } public partial class ZoneData { #if UNITY_EDITOR private static void AddButtonOnTitleBarGUI(Action buttonAction, EditorIcon icon = null) { icon ??= EditorIcons.MagnifyingGlass; if (SirenixEditorGUI.ToolbarButton(icon)) { buttonAction?.Invoke(); } } #endif } public partial class ZoneData { [Title("Scene Info")] [ValueDropdown("GetAllSceneNames"), PropertyOrder(-1)] [InfoBox("当前场景和ZoneData的关联场景不匹配!", InfoMessageType.Error, VisibleIf = "@!IsCurrentSceneMatch")] public string sceneName; public bool IsCurrentSceneMatch => UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == sceneName; #if UNITY_EDITOR private List GetAllSceneNames() { List sceneNames = new List(); foreach (var scene in EditorBuildSettings.scenes) { if (scene.enabled) { string name = System.IO.Path.GetFileNameWithoutExtension(scene.path); sceneNames.Add(name); } } return sceneNames; } #endif } public partial class ZoneData { private void CollectSpawnPoints(List list, string groupName) { ZoneManager zoneManager = ZoneManager.Instance; zoneManager.RebuildMapData(); list.Clear(); if (zoneManager.spawnPoints.TryGetValue(groupName, out List points)) { for (int i = 0; i < points.Count; i++) { list.Add(new SpawnPointKey(groupName, i)); } } } } public partial class ZoneData { [Title("Spawn Settings")] [Title("Player", HorizontalLine = false, Bold = false)] [ListDrawerSettings(ShowIndexLabels = false, AddCopiesLastElement = true), PropertyOrder(10), LabelText("Player Spawns")] public List playerSpawns = new List() { new SpawnPointKey("Player", 0) }; } public partial class ZoneData { [SerializeField, HideInInspector] public Dictionary enemySpawns = new Dictionary(); #if UNITY_EDITOR [HideInInspector] private List selectedGroupsToSync = new List(); [Title("Enemy", HorizontalLine = false)] [ShowInInspector] [ListDrawerSettings( OnTitleBarGUI = "DrawEnemySpawnTitleBarGUI", ShowFoldout = true, CustomRemoveIndexFunction = "RemoveEnemySpawnsIndex", CustomRemoveElementFunction = "RemoveEnemySpawnsElement")] [LabelText("Enemy Spawns"), PropertyOrder(12)] private List EnemySpawnsInEditor { get => GetSortedMappings(); set { enemySpawns = new Dictionary(); foreach (EnemySpawnMapping item in value) { enemySpawns[new SpawnPointKey(item.group, item.index)] = item.enemyID; } EditorUtility.SetDirty(this); } } private void OnEnemySyncSettingsChanged() { ZoneManager zoneManager = ZoneManager.Instance; zoneManager.RebuildMapData(); Dictionary newDict = new Dictionary(); foreach (string group in selectedGroupsToSync) { if (zoneManager.spawnPoints.TryGetValue(group, out List points)) { for (int i = 0; i < points.Count; i++) { SpawnPointKey key = new SpawnPointKey(group, i); newDict[key] = enemySpawns != null ? enemySpawns.GetValueOrDefault(key, "") : ""; } } } enemySpawns = newDict; EditorUtility.SetDirty(this); } private List GetSortedMappings() { List list = new List(); if (enemySpawns == null) return list; list.AddRange(enemySpawns.Select(kvp => new EnemySpawnMapping(kvp.Key, kvp.Value, this))); list.Sort((a, b) => { int comp = string.Compare(a.group, b.group, System.StringComparison.Ordinal); if (comp != 0) return comp; return a.index.CompareTo(b.index); }); return list; } private void RemoveEnemySpawnsElement(EnemySpawnMapping element) { SpawnPointKey key = new SpawnPointKey(element.group, element.index); if (enemySpawns.Remove(key)) { EditorUtility.SetDirty(this); } } private void RemoveEnemySpawnsIndex(int index) { var list = GetSortedMappings(); if (index >= 0 && index < list.Count) { EnemySpawnMapping item = list[index]; SpawnPointKey key = new SpawnPointKey(item.group, item.index); if (enemySpawns.Remove(key)) { EditorUtility.SetDirty(this); } } } public void UpdateDictionaryEntry(string group, int index, string newEnemyID) { SpawnPointKey key = new SpawnPointKey(group, index); if (enemySpawns.ContainsKey(key)) { enemySpawns[key] = newEnemyID; EditorUtility.SetDirty(this); } } private void DrawEnemySpawnTitleBarGUI() => AddButtonOnTitleBarGUI(OpenEnemySyncSelector); private void OpenEnemySyncSelector() { ZoneManager zoneManager = ZoneManager.Instance; zoneManager.RebuildMapData(); GroupSyncPopup popup = new GroupSyncPopup(zoneManager.spawnPoints.Keys, selectedGroupsToSync, (result) => { selectedGroupsToSync = result; OnEnemySyncSettingsChanged(); }); OdinEditorWindow window = OdinEditorWindow.InspectObject(popup); float width = 400; float height = 200; var centerRect = GUIHelper.GetEditorWindowRect().AlignCenter(width, height); window.position = centerRect; window.ShowPopup(); } private class GroupSyncPopup { [LabelText("Available Groups")] [ListDrawerSettings(IsReadOnly = true, ShowIndexLabels = false, HideAddButton = true, HideRemoveButton = true)] [TableList(AlwaysExpanded = true, HideToolbar = true)] [ShowInInspector, PropertyOrder(1)] public List toggles = new List(); private readonly System.Action> onConfirm; public GroupSyncPopup(IEnumerable available, List current, System.Action> onConfirm) { this.onConfirm = onConfirm; foreach (string name in available) { toggles.Add(new GroupItem(name, current.Contains(name))); } } [Button, PropertyOrder(2)] public void Confirm() { List result = toggles.Where(t => t.sync).Select(t => t.name).ToList(); this.onConfirm?.Invoke(result); GUIHelper.CurrentWindow?.Close(); } public class GroupItem { [HideLabel, TableColumnWidth(30, Resizable = false)] public bool sync; [HideLabel, ReadOnly] public string name; public GroupItem(string name, bool sync) { this.name = name; this.sync = sync; } } } #endif } public partial class ZoneData { [Title("Specified Locations", HorizontalLine = false)] [PropertyOrder(20)] [LabelText("Exit Spawns")] [ListDrawerSettings(ShowIndexLabels = false, OnTitleBarGUI = "DrawExitSpawnsTitleBarGUI")] public List exitSpawns = new List(); [PropertyOrder(21)] [LabelText("Mechanical Table Spawns")] [ListDrawerSettings(ShowIndexLabels = false, OnTitleBarGUI = "DrawMechanicalTableSpawnsTitleBarGUI")] public List mechanicalTableSpawns = new List(); [PropertyOrder(22)] [LabelText("Logistics Center Spawns")] [ListDrawerSettings(ShowIndexLabels = false, OnTitleBarGUI = "DrawLogisticsCenterSpawnsTitleBarGUI")] public List logisticsCenterSpawns = new List(); #if UNITY_EDITOR private void DrawExitSpawnsTitleBarGUI() => AddButtonOnTitleBarGUI(() => CollectSpawnPoints(exitSpawns, "Exit"), EditorIcons.Refresh); private void DrawMechanicalTableSpawnsTitleBarGUI() => AddButtonOnTitleBarGUI(() => CollectSpawnPoints(mechanicalTableSpawns, "MechanicalTable"), EditorIcons.Refresh); private void DrawLogisticsCenterSpawnsTitleBarGUI() => AddButtonOnTitleBarGUI(() => CollectSpawnPoints(logisticsCenterSpawns, "LogisticsCenter"), EditorIcons.Refresh); #endif } public partial class ZoneData { [Title("Random Locations", HorizontalLine = false)] [ListDrawerSettings(ShowIndexLabels = false, AddCopiesLastElement = true), PropertyOrder(50), LabelText("Random Location Spawns")] public List randomLocationSpawns = new List(); [ValueDropdown("GetLocationIDs"), PropertyOrder(51), LabelText("Available Random Locations")] public List availableRandomLocations = new List(); public List GetLocationIDs() { return new List() { "Factory", "Warehouse", "Office", "Laboratory", "Hangar", "Storage" } .Exclude(availableRandomLocations).ToList(); } } }