375 lines
13 KiB
C#
375 lines
13 KiB
C#
|
|
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<SpawnPointKey>
|
|||
|
|
{
|
|||
|
|
[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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnEnemyIDChanged()
|
|||
|
|
{
|
|||
|
|
if (parent != null)
|
|||
|
|
{
|
|||
|
|
parent.UpdateDictionaryEntry(group, index, enemyID);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private IEnumerable<string> GetEnemyKeys()
|
|||
|
|
{
|
|||
|
|
return MainGameBaseCollection.Instance.enemiesCollection.Keys;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public partial class ZoneData
|
|||
|
|
{
|
|||
|
|
private static void AddButtonOnTitleBarGUI(Action buttonAction, EditorIcon icon = null)
|
|||
|
|
{
|
|||
|
|
icon ??= EditorIcons.MagnifyingGlass;
|
|||
|
|
if (SirenixEditorGUI.ToolbarButton(icon))
|
|||
|
|
{
|
|||
|
|
buttonAction?.Invoke();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
|
|||
|
|
private List<string> GetAllSceneNames()
|
|||
|
|
{
|
|||
|
|
List<string> sceneNames = new List<string>();
|
|||
|
|
foreach (var scene in EditorBuildSettings.scenes)
|
|||
|
|
{
|
|||
|
|
if (scene.enabled)
|
|||
|
|
{
|
|||
|
|
string name = System.IO.Path.GetFileNameWithoutExtension(scene.path);
|
|||
|
|
sceneNames.Add(name);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return sceneNames;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public partial class ZoneData
|
|||
|
|
{
|
|||
|
|
private void CollectSpawnPoints(List<SpawnPointKey> list, string groupName)
|
|||
|
|
{
|
|||
|
|
ZoneManager zoneManager = ZoneManager.Instance;
|
|||
|
|
zoneManager.RebuildMapData();
|
|||
|
|
list.Clear();
|
|||
|
|
if (zoneManager.spawnPoints.TryGetValue(groupName, out List<SpawnPoint> 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<SpawnPointKey> playerSpawns = new List<SpawnPointKey>() { new SpawnPointKey("Player", 0) };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public partial class ZoneData
|
|||
|
|
{
|
|||
|
|
[SerializeField, HideInInspector]
|
|||
|
|
public Dictionary<SpawnPointKey, string> enemySpawns = new Dictionary<SpawnPointKey, string>();
|
|||
|
|
|
|||
|
|
#if UNITY_EDITOR
|
|||
|
|
[HideInInspector]
|
|||
|
|
private List<string> selectedGroupsToSync = new List<string>();
|
|||
|
|
|
|||
|
|
[Title("Enemy", HorizontalLine = false)]
|
|||
|
|
[ShowInInspector]
|
|||
|
|
[ListDrawerSettings(
|
|||
|
|
OnTitleBarGUI = "DrawEnemySpawnTitleBarGUI",
|
|||
|
|
ShowFoldout = true,
|
|||
|
|
CustomRemoveIndexFunction = "RemoveEnemySpawnsIndex",
|
|||
|
|
CustomRemoveElementFunction = "RemoveEnemySpawnsElement")]
|
|||
|
|
[LabelText("Enemy Spawns"), PropertyOrder(12)]
|
|||
|
|
private List<EnemySpawnMapping> EnemySpawnsInEditor
|
|||
|
|
{
|
|||
|
|
get => GetSortedMappings();
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
enemySpawns = new Dictionary<SpawnPointKey, string>();
|
|||
|
|
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<SpawnPointKey, string> newDict = new Dictionary<SpawnPointKey, string>();
|
|||
|
|
foreach (string group in selectedGroupsToSync)
|
|||
|
|
{
|
|||
|
|
if (zoneManager.spawnPoints.TryGetValue(group, out List<SpawnPoint> 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<EnemySpawnMapping> GetSortedMappings()
|
|||
|
|
{
|
|||
|
|
List<EnemySpawnMapping> list = new List<EnemySpawnMapping>();
|
|||
|
|
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<GroupItem> toggles = new List<GroupItem>();
|
|||
|
|
|
|||
|
|
private readonly System.Action<List<string>> onConfirm;
|
|||
|
|
|
|||
|
|
public GroupSyncPopup(IEnumerable<string> available, List<string> current, System.Action<List<string>> onConfirm)
|
|||
|
|
{
|
|||
|
|
this.onConfirm = onConfirm;
|
|||
|
|
foreach (string name in available)
|
|||
|
|
{
|
|||
|
|
toggles.Add(new GroupItem(name, current.Contains(name)));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[Button, PropertyOrder(2)]
|
|||
|
|
public void Confirm()
|
|||
|
|
{
|
|||
|
|
List<string> 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<SpawnPointKey> exitSpawns = new List<SpawnPointKey>() { new SpawnPointKey("Exit", 0) };
|
|||
|
|
private void DrawExitSpawnsTitleBarGUI() =>
|
|||
|
|
AddButtonOnTitleBarGUI(()=>CollectSpawnPoints(exitSpawns, "Exit"), EditorIcons.Refresh);
|
|||
|
|
|
|||
|
|
|
|||
|
|
[PropertySpace]
|
|||
|
|
[PropertyOrder(21)]
|
|||
|
|
[LabelText("Tech Center Spawns")]
|
|||
|
|
[ListDrawerSettings(ShowIndexLabels = false, OnTitleBarGUI = "DrawTechCenterSpawnsTitleBarGUI")]
|
|||
|
|
public List<SpawnPointKey> techCenterSpawns = new List<SpawnPointKey>();
|
|||
|
|
private void DrawTechCenterSpawnsTitleBarGUI() =>
|
|||
|
|
AddButtonOnTitleBarGUI(()=>CollectSpawnPoints(techCenterSpawns, "TechCenter"), EditorIcons.Refresh);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public partial class ZoneData
|
|||
|
|
{
|
|||
|
|
[Title("Random Locations", HorizontalLine = false)]
|
|||
|
|
[ListDrawerSettings(ShowIndexLabels = false, AddCopiesLastElement = true), PropertyOrder(50), LabelText("Random Location Spawns")]
|
|||
|
|
public List<SpawnPointKey> randomLocationSpawns = new List<SpawnPointKey>();
|
|||
|
|
|
|||
|
|
[ValueDropdown("GetLocationIDs"), PropertyOrder(51), LabelText("Available Random Locations")]
|
|||
|
|
public List<string> availableRandomLocations = new List<string>();
|
|||
|
|
|
|||
|
|
public List<string> GetLocationIDs()
|
|||
|
|
{
|
|||
|
|
return new List<string>() { "Factory", "Warehouse", "Office", "Laboratory", "Hangar", "Storage" }
|
|||
|
|
.Exclude(availableRandomLocations).ToList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|