RiftMayhem/Assets/Scripts/Editor/ability-effect-editor.cs

420 lines
15 KiB
C#

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;
public partial class AbilityEffectEditorWindow : EditorWindow
{
private Vector2 scrollPosition;
private string searchQuery = "";
private AbilityCreationData abilityData;
private EffectCreationData effectData;
private Type selectedAbilityType;
private Type selectedEffectType;
private Dictionary<string, Type> abilityTypes;
private Dictionary<string, Type> effectTypes;
// Tabs
private int selectedTab = 0;
private readonly string[] tabNames = { "Abilities", "Effects" };
[MenuItem("Tools/Ability & Effect Editor")]
public static void ShowWindow()
{
GetWindow<AbilityEffectEditorWindow>("Ability & Effect Editor");
}
private void OnEnable()
{
InitializeData();
CacheTypes();
}
private void InitializeData()
{
abilityData = new AbilityCreationData();
effectData = new EffectCreationData();
}
private void CacheTypes()
{
// Cache ability types
abilityTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsClass && !type.IsAbstract && typeof(BaseAbility).IsAssignableFrom(type))
.ToDictionary(type => type.Name, type => type);
// Cache effect types
effectTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsClass && !type.IsAbstract && typeof(BaseEffect).IsAssignableFrom(type))
.ToDictionary(type => type.Name, type => type);
}
private void OnGUI()
{
DrawHeader();
DrawSearchBar();
selectedTab = GUILayout.Toolbar(selectedTab, tabNames);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
switch (selectedTab)
{
case 0: // Abilities Tab
DrawAbilityCreation();
EditorGUILayout.Space(20);
DrawExistingAbilities();
break;
case 1: // Effects Tab
DrawEffectCreation();
EditorGUILayout.Space(20);
DrawExistingEffects();
break;
}
EditorGUILayout.EndScrollView();
}
private void DrawHeader()
{
EditorGUILayout.Space(10);
GUILayout.Label("Ability Effect Editor", EditorStyles.boldLabel);
EditorGUILayout.Space(5);
}
private void DrawAbilityCreation()
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Create New Ability", EditorStyles.boldLabel);
DrawAbilityTypeSelection();
if (selectedAbilityType != null)
{
DrawBasicFields();
DrawTypeSpecificFields();
EditorGUILayout.Space(10);
if (GUILayout.Button("Create Ability"))
{
CreateAbility();
}
}
EditorGUILayout.EndVertical();
}
private void DrawBasicFields()
{
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Basic Settings", EditorStyles.boldLabel);
abilityData.name = EditorGUILayout.TextField("Name", abilityData.name);
abilityData.description = EditorGUILayout.TextArea(abilityData.description, GUILayout.Height(60));
abilityData.icon = (Sprite)EditorGUILayout.ObjectField("Icon", abilityData.icon, typeof(Sprite), false);
abilityData.animationType = (AbilityAnimationType)EditorGUILayout.EnumPopup("Animation Type", abilityData.animationType);
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Costs and Timing", EditorStyles.boldLabel);
abilityData.manaCost = EditorGUILayout.FloatField("Mana Cost", abilityData.manaCost);
abilityData.healthCost = EditorGUILayout.FloatField("Health Cost", abilityData.healthCost);
abilityData.cooldown = EditorGUILayout.FloatField("Cooldown", abilityData.cooldown);
abilityData.castTime = EditorGUILayout.FloatField("Cast Time", abilityData.castTime);
abilityData.castableWhileMoving = EditorGUILayout.Toggle("Castable While Moving", abilityData.castableWhileMoving);
}
private void DrawTypeSpecificFields()
{
if (selectedAbilityType == null) return;
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Type Specific Settings", EditorStyles.boldLabel);
if (selectedAbilityType == typeof(ProjectileAbility))
{
DrawProjectileFields();
}
else if (selectedAbilityType == typeof(AreaOfEffectAbility))
{
DrawAreaOfEffectFields();
}
else if (selectedAbilityType == typeof(ChanneledAbility))
{
DrawChanneledFields();
}
}
private void DrawProjectileFields()
{
float projectileSpeed = abilityData.GetOrCreateTypeSpecific<float>("projectileSpeed");
projectileSpeed = EditorGUILayout.FloatField("Projectile Speed", projectileSpeed);
abilityData.typeSpecificData["projectileSpeed"] = projectileSpeed;
float lifeSpan = abilityData.GetOrCreateTypeSpecific<float>("lifeSpan");
lifeSpan = EditorGUILayout.FloatField("Life Span", lifeSpan);
abilityData.typeSpecificData["lifeSpan"] = lifeSpan;
bool canPierce = abilityData.GetOrCreateTypeSpecific<bool>("canPierce");
canPierce = EditorGUILayout.Toggle("Can Pierce", canPierce);
abilityData.typeSpecificData["canPierce"] = canPierce;
GameObject projectilePrefab = abilityData.GetOrCreateTypeSpecific<GameObject>("projectilePrefab");
projectilePrefab = (GameObject)EditorGUILayout.ObjectField("Projectile Prefab",
projectilePrefab, typeof(GameObject), false);
abilityData.typeSpecificData["projectilePrefab"] = projectilePrefab;
}
private void DrawAreaOfEffectFields()
{
float radius = abilityData.GetOrCreateTypeSpecific<float>("radius");
radius = EditorGUILayout.FloatField("Radius", radius);
abilityData.typeSpecificData["radius"] = radius;
float duration = abilityData.GetOrCreateTypeSpecific<float>("duration");
duration = EditorGUILayout.FloatField("Duration", duration);
abilityData.typeSpecificData["duration"] = duration;
GameObject aoePrefab = abilityData.GetOrCreateTypeSpecific<GameObject>("aoePrefab");
aoePrefab = (GameObject)EditorGUILayout.ObjectField("AoE Prefab",
aoePrefab, typeof(GameObject), false);
abilityData.typeSpecificData["aoePrefab"] = aoePrefab;
}
private void DrawChanneledFields()
{
float channelDuration = abilityData.GetOrCreateTypeSpecific<float>("channelDuration");
channelDuration = EditorGUILayout.FloatField("Channel Duration", channelDuration);
abilityData.typeSpecificData["channelDuration"] = channelDuration;
bool canMove = abilityData.GetOrCreateTypeSpecific<bool>("canMove");
canMove = EditorGUILayout.Toggle("Can Move While Channeling", canMove);
abilityData.typeSpecificData["canMove"] = canMove;
}
private void CreateAbility()
{
if (selectedAbilityType == null)
{
EditorUtility.DisplayDialog("Error", "Please select an ability type!", "OK");
return;
}
if (string.IsNullOrEmpty(abilityData.name))
{
EditorUtility.DisplayDialog("Error", "Ability name cannot be empty!", "OK");
return;
}
BaseAbility ability = (BaseAbility)CreateInstance(selectedAbilityType);
ability.name = abilityData.name;
ability.Icon = abilityData.icon;
ability.animationType = abilityData.animationType;
ability.manaCost = abilityData.manaCost;
ability.healthCost = abilityData.healthCost;
ability.cooldown = abilityData.cooldown;
ability.castTime = abilityData.castTime;
ability.castableWhileMoving = abilityData.castableWhileMoving;
ApplyTypeSpecificData(ability);
string path = EditorUtility.SaveFilePanelInProject(
"Save Ability",
abilityData.name,
"asset",
"Save ability asset"
);
if (!string.IsNullOrEmpty(path))
{
AssetDatabase.CreateAsset(ability, path);
AssetDatabase.SaveAssets();
EditorUtility.FocusProjectWindow();
Selection.activeObject = ability;
abilityData = new AbilityCreationData();
selectedAbilityType = null;
}
}
private void ApplyTypeSpecificData(BaseAbility ability)
{
if (ability is ProjectileAbility projectileAbility)
{
projectileAbility.projectileSpeed = abilityData.GetOrCreateTypeSpecific<float>("projectileSpeed");
projectileAbility.lifeSpan = abilityData.GetOrCreateTypeSpecific<float>("lifeSpan");
projectileAbility.canPierce = abilityData.GetOrCreateTypeSpecific<bool>("canPierce");
projectileAbility.projectilePrefab = abilityData.GetOrCreateTypeSpecific<GameObject>("projectilePrefab");
}
else if (ability is AreaOfEffectAbility aoeAbility)
{
aoeAbility.radius = abilityData.GetOrCreateTypeSpecific<float>("radius");
aoeAbility.lifeSpan = abilityData.GetOrCreateTypeSpecific<float>("duration");
aoeAbility.aoePrefab = abilityData.GetOrCreateTypeSpecific<GameObject>("aoePrefab");
}
else if (ability is ChanneledAbility channeledAbility)
{
channeledAbility.duration = abilityData.GetOrCreateTypeSpecific<float>("channelDuration");
channeledAbility.castableWhileMoving = abilityData.GetOrCreateTypeSpecific<bool>("canMove");
}
}
private void DrawAbilityTypeSelection()
{
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Ability Type", EditorStyles.boldLabel);
string[] typeNames = abilityTypes.Keys.ToArray();
int currentIndex = Array.IndexOf(typeNames, selectedAbilityType?.Name ?? "");
int newIndex = EditorGUILayout.Popup("Select Type", currentIndex, typeNames);
if (newIndex != currentIndex && newIndex >= 0)
{
selectedAbilityType = abilityTypes[typeNames[newIndex]];
abilityData.typeSpecificData.Clear();
}
}
private void DrawAbilityItem(BaseAbility ability)
{
EditorGUILayout.BeginHorizontal();
if (ability.Icon != null)
{
GUILayout.Label(AssetPreview.GetAssetPreview(ability.Icon), GUILayout.Width(40), GUILayout.Height(40));
}
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(ability.name, EditorStyles.boldLabel);
EditorGUILayout.LabelField($"Type: {ability.GetType().Name}");
EditorGUILayout.EndVertical();
if (GUILayout.Button("Clone", GUILayout.Width(50)))
{
CloneAbility(ability);
}
if (GUILayout.Button("Edit", GUILayout.Width(50)))
{
Selection.activeObject = ability;
EditorUtility.FocusProjectWindow();
}
EditorGUILayout.EndHorizontal();
}
private void CloneAbility(BaseAbility sourceAbility)
{
string newName = $"{sourceAbility.name}_Clone";
string path = EditorUtility.SaveFilePanelInProject(
"Save Cloned Ability",
newName,
"asset",
"Save cloned ability asset"
);
if (string.IsNullOrEmpty(path)) return;
BaseAbility newAbility = (BaseAbility)CreateInstance(sourceAbility.GetType());
EditorUtility.CopySerialized(sourceAbility, newAbility);
newAbility.name = newName;
AssetDatabase.CreateAsset(newAbility, path);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.FocusProjectWindow();
Selection.activeObject = newAbility;
}
private void DrawExistingAbilities()
{
EditorGUILayout.LabelField("Existing Abilities", EditorStyles.boldLabel);
var abilities = GetAllAbilities();
foreach (var ability in abilities)
{
if (string.IsNullOrEmpty(searchQuery) || ability.name.ToLower().Contains(searchQuery.ToLower()))
{
EditorGUILayout.BeginVertical("box");
DrawAbilityItem(ability);
EditorGUILayout.EndVertical();
}
}
}
private BaseAbility[] GetAllAbilities()
{
return AssetDatabase.FindAssets("t:BaseAbility")
.Select(guid => AssetDatabase.LoadAssetAtPath<BaseAbility>(AssetDatabase.GUIDToAssetPath(guid)))
.ToArray();
}
private void DrawSearchBar()
{
EditorGUILayout.BeginHorizontal();
searchQuery = EditorGUILayout.TextField("Search", searchQuery);
if (GUILayout.Button("Clear", GUILayout.Width(50)))
searchQuery = "";
EditorGUILayout.EndHorizontal();
}
private void DrawEffectListForAbility(List<BaseEffect> effects)
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Effects", EditorStyles.boldLabel);
for (int i = 0; i < effects.Count; i++)
{
EditorGUILayout.BeginHorizontal();
effects[i] = (BaseEffect)EditorGUILayout.ObjectField(effects[i], typeof(BaseEffect), false);
if (GUILayout.Button("Remove", GUILayout.Width(60)))
{
effects.RemoveAt(i);
break;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.Space();
if (GUILayout.Button("Add Effect"))
{
effects.Add(null);
}
if (GUILayout.Button("Create New Effect"))
{
selectedTab = 1; // Switch to Effects tab
effectData = new EffectCreationData();
}
EditorGUILayout.EndVertical();
}
}
[System.Serializable]
public class EffectCreationData
{
public string name;
public string description;
public float duration;
public bool applyToTargetsHit = true;
public bool applyToSelf;
public List<GameTag> tags = new List<GameTag>();
public List<StatInfluence> statInfluences = new List<StatInfluence>();
public SerializedDictionary<string, object> typeSpecificData = new SerializedDictionary<string, object>();
public T GetOrCreateTypeSpecific<T>(string key)
{
if (!typeSpecificData.ContainsKey(key))
{
if (typeof(T) == typeof(List<StatInfluence>))
typeSpecificData[key] = new List<StatInfluence>();
else
typeSpecificData[key] = default(T);
}
return (T)typeSpecificData[key];
}
}