Ability - Effect Editor window

This commit is contained in:
Pedro Gomes 2024-12-04 19:22:51 +00:00
parent f92d5d5866
commit 6ba55cb483
35 changed files with 1610 additions and 3 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e0c2df9140a6fce45a9576a1db3e0d43
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 03064a0d3f6200145b50c40e4cd12615, type: 3}
m_Name: TestingAwakenings
m_EditorClassIdentifier:
data:
Name:
Description:
CurrentPoints: 0
MaxPoints: 1
requiredAwakenings: []
StrengthBonus: 0
AgilityBonus: 0
IntelligenceBonus: 0
SpiritBonus: 0
VitalityBonus: 0
StrengthPercentBonus: 0
AgilityPercentBonus: 0
IntelligencePercentBonus: 0
SpiritPercentBonus: 0
VitalityPercentBonus: 0
AttackDamageBonus: 0
SpellDamageBonus: 0
CritChanceBonus: 0
CritDamageBonus: 0
MaxHealthBonus: 0
ArmorBonus: 0
MagicResistanceBonus: 0
AttackDamagePercentBonus: 0
SpellDamagePercentBonus: 0
CritChancePercentBonus: 0
CritDamagePercentBonus: 0
MaxHealthPercentBonus: 0
ArmorPercentBonus: 0
MagicResistancePercentBonus: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bfc36a68112b4534c8eb33a78518ab6c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 307841cd77271a8468c442f4a604e862
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class BaseAbilityData
{
public Sprite Icon;
public List<TargetTag> targettingTags = new List<TargetTag>();
public List<GameTag> tags = new List<GameTag>();
public List<BaseEffect> abilityEffects = new List<BaseEffect>();
[Space]
public float castTime;
public float manaCost;
public float healthCost = 0;
public float classResourceCost = 0;
public float cooldown;
public bool castableWhileMoving;
public AbilityAnimationType animationType;
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 85e3596f5bd1bf44d8343b3f2b4cd7ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9b2dc875bac316e49b4963b666aafd44
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class BaseAbilityInstance
{
public BaseAbilityData abilityData;
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ecbc603708f7b784ab0a4bc2cce24156
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -93,8 +93,6 @@ public class InstantValueEffect : BaseEffect
else if(stats.secondaryStatsDictionary.TryGetValue(statInfluence.statTag.name.ToLower(), out CharacterStat secondaryStat))
{
finalValue += secondaryStat.Value * statInfluence.percentInfluence;
}
if (statInfluence.statTag.name.ToLower().Contains("Attack")) damageType = DamageType.Attack;
else if (statInfluence.statTag.name.ToLower().Contains("Spell")) damageType = DamageType.Spell;

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b3e9fe72bbbe46648920a316a683101f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,134 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Awakening", menuName = "RiftMayhem/AwakeningSystem/Awakenings/new BaseAwakening", order = 0)]
public class Awakening : ScriptableObject
{
public AwakeningData data;
public bool AllRequirementsMet(List<Awakening> unlockedAwakenings)
{
for (int i = 0; i < data.requiredAwakenings.Count; i++)
{
if (!unlockedAwakenings.Contains(data.requiredAwakenings[i])) return false;
}
return true;
}
}
[System.Serializable]
public class AwakeningInstance
{
public AwakeningData data;
public bool AllRequirementsMet(List<Awakening> unlockedAwakenings)
{
for (int i = 0; i < data.requiredAwakenings.Count; i++)
{
if (!unlockedAwakenings.Contains(data.requiredAwakenings[i])) return false;
}
return true;
}
}
[System.Serializable]
public class AwakeningData
{
public string Name;
public string Description;
public int CurrentPoints;
public int MaxPoints;
public List<Awakening> requiredAwakenings = new List<Awakening>();
[Space]
public int StrengthBonus;
public int AgilityBonus;
public int IntelligenceBonus;
public int SpiritBonus;
public int VitalityBonus;
[Space]
public float StrengthPercentBonus;
public float AgilityPercentBonus;
public float IntelligencePercentBonus;
public float SpiritPercentBonus;
public float VitalityPercentBonus;
[Space]
public int AttackDamageBonus;
public int SpellDamageBonus;
public int CritChanceBonus;
public int CritDamageBonus;
public int MaxHealthBonus;
public int ArmorBonus;
public int MagicResistanceBonus;
[Space]
public float AttackDamagePercentBonus;
public float SpellDamagePercentBonus;
public float CritChancePercentBonus;
public float CritDamagePercentBonus;
public float MaxHealthPercentBonus;
public float ArmorPercentBonus;
public float MagicResistancePercentBonus;
public AwakeningData()
{
Name = "";
Description = "";
CurrentPoints = 0;
MaxPoints = 1;
requiredAwakenings = new List<Awakening>();
}
public AwakeningData(string name, string description)
{
Name = name;
Description = description;
CurrentPoints = 0;
MaxPoints = 1;
requiredAwakenings = new List<Awakening>();
}
public AwakeningData(string name, string description, int currentPoints, int totalPoints, List<Awakening> requirements)
{
Name = name;
Description = description;
CurrentPoints = currentPoints;
MaxPoints = totalPoints;
requiredAwakenings = requirements;
}
public AwakeningData(AwakeningData data)
{
this.Name = data.Name;
this.Description = data.Description;
this.CurrentPoints = data.CurrentPoints;
this.MaxPoints = data.MaxPoints;
this.requiredAwakenings = data.requiredAwakenings;
this.StrengthBonus = data.StrengthBonus;
this.AgilityBonus = data.AgilityBonus;
this.IntelligenceBonus = data.IntelligenceBonus;
this.SpiritBonus = data.SpiritBonus;
this.VitalityBonus = data.VitalityBonus;
this.StrengthPercentBonus = data.StrengthPercentBonus;
this.AgilityPercentBonus = data.AgilityPercentBonus;
this.IntelligencePercentBonus = data.IntelligencePercentBonus;
this.SpiritPercentBonus = data.SpiritPercentBonus;
this.VitalityPercentBonus = data.VitalityPercentBonus;
this.AttackDamageBonus = data.AttackDamageBonus;
this.SpellDamageBonus = data.SpellDamageBonus;
this.CritChanceBonus = data.CritChanceBonus;
this.CritDamageBonus = data.CritDamageBonus;
this.MaxHealthBonus = data.MaxHealthBonus;
this.ArmorBonus = data.ArmorBonus;
this.MagicResistanceBonus = data.MagicResistanceBonus;
this.AttackDamagePercentBonus = data.AttackDamagePercentBonus;
this.SpellDamagePercentBonus = data.SpellDamagePercentBonus;
this.CritChancePercentBonus = data.CritChancePercentBonus;
this.CritDamagePercentBonus = data.CritDamagePercentBonus;
this.MaxHealthPercentBonus = data.MaxHealthPercentBonus;
this.ArmorPercentBonus = data.ArmorPercentBonus;
this.MagicResistancePercentBonus = data.MagicResistancePercentBonus;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03064a0d3f6200145b50c40e4cd12615
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bac7dcb65e711114fa3203ffde968f74
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpecificDamageIncreaseAwakening : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12323d49914f700448c37231a033bb2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClassAwakenings
{
public List<Awakening> AllAvailableAwakenings = new List<Awakening>();
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9485a30ee3b5f804d959e444e9d5130c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAwakenings : MonoBehaviour
{
private List<BaseAbility> unlockedAbilities = new List<BaseAbility>();
private List<Awakening> unlockedAwakenings = new List<Awakening>();
public void UnlockAbility(BaseAbility ability)
{
if (unlockedAbilities.Contains(ability)) return;
unlockedAbilities.Add(ability);
}
public void UnlockAwakening(Awakening awakening)
{
if (unlockedAwakenings.Contains(awakening)) return;
if (!awakening.AllRequirementsMet(unlockedAwakenings)) return;
unlockedAwakenings.Add(awakening);
}
public bool IsAbilityUnlocked(BaseAbility ability)
{
return unlockedAbilities.Contains(ability);
}
public bool IsAwakeningUnlocked(Awakening awakening)
{
return unlockedAwakenings.Contains(awakening);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a860d52346f7abb4fbc40f3477364ae9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,70 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;
public partial class AbilityEditorWindow : EditorWindow
{
private Vector2 scrollPosition;
private string searchQuery = "";
private AbilityCreationData abilityData;
private Type selectedAbilityType;
private Dictionary<string, Type> abilityTypes;
[MenuItem("Tools/Ability Editor")]
public static void ShowWindow()
{
GetWindow<AbilityEditorWindow>("Ability Editor");
}
private void OnEnable()
{
InitializeData();
CacheAbilityTypes();
}
private void InitializeData()
{
abilityData = new AbilityCreationData();
}
private void CacheAbilityTypes()
{
// Cache all ability types that inherit from BaseAbility
abilityTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsClass && !type.IsAbstract && typeof(BaseAbility).IsAssignableFrom(type))
.ToDictionary(type => type.Name, type => type);
}
private void OnGUI()
{
DrawHeader();
DrawSearchBar();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
DrawAbilityCreation();
EditorGUILayout.Space(20);
DrawExistingAbilities();
EditorGUILayout.EndScrollView();
}
private void DrawHeader()
{
EditorGUILayout.Space(10);
GUILayout.Label("Ability Editor", EditorStyles.boldLabel);
EditorGUILayout.Space(5);
}
private void DrawSearchBar()
{
EditorGUILayout.BeginHorizontal();
searchQuery = EditorGUILayout.TextField("Search", searchQuery);
if (GUILayout.Button("Clear", GUILayout.Width(50)))
searchQuery = "";
EditorGUILayout.EndHorizontal();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75d10d37171d2d24d8d4069eba82bbe3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,133 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;
public partial class AbilityEditorWindow
{
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 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 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;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86bffbad8290be14f98db3d5ac63a370
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class AbilityCreationData
{
public string name;
public string description;
public Sprite icon;
public AbilityAnimationType animationType;
public float manaCost;
public float healthCost;
public float cooldown;
public float castTime;
public bool castableWhileMoving;
public SerializedDictionary<string, object> typeSpecificData = new SerializedDictionary<string, object>();
public T GetOrCreateTypeSpecific<T>(string key)
{
if (!typeSpecificData.ContainsKey(key))
{
typeSpecificData[key] = default(T);
}
return (T)typeSpecificData[key];
}
}
[System.Serializable]
public class SerializedDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
[SerializeField]
private List<TKey> keys = new List<TKey>();
[SerializeField]
private List<TValue> values = new List<TValue>();
public void OnBeforeSerialize()
{
keys.Clear();
values.Clear();
foreach(KeyValuePair<TKey, TValue> pair in this)
{
keys.Add(pair.Key);
values.Add(pair.Value);
}
}
public void OnAfterDeserialize()
{
this.Clear();
if(keys.Count != values.Count)
throw new System.Exception($"There are {keys.Count} keys and {values.Count} values after deserialization. Make sure that both key and value types are serializable.");
for(int i = 0; i < keys.Count; i++)
this.Add(keys[i], values[i]);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36c5f08218aeb2c44b191463947d6160
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,149 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;
public partial class AbilityEditorWindow
{
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 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 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 BaseAbility[] GetAllAbilities()
{
return AssetDatabase.FindAssets("t:BaseAbility")
.Select(guid => AssetDatabase.LoadAssetAtPath<BaseAbility>(AssetDatabase.GUIDToAssetPath(guid)))
.ToArray();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a19e286df3408fa4f9e2f82a2d93d2b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,420 @@
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];
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55d583c683871fd47ad6807ca5ce8edd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,177 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;
public partial class AbilityEffectEditorWindow
{
private void DrawEffectCreation()
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Create New Effect", EditorStyles.boldLabel);
DrawEffectTypeSelection();
if (selectedEffectType != null)
{
DrawEffectBasicFields();
DrawEffectTypeSpecificFields();
EditorGUILayout.Space(10);
if (GUILayout.Button("Create Effect"))
{
CreateEffect();
}
}
EditorGUILayout.EndVertical();
}
private void DrawEffectTypeSelection()
{
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Effect Type", EditorStyles.boldLabel);
string[] typeNames = effectTypes.Keys.ToArray();
int currentIndex = Array.IndexOf(typeNames, selectedEffectType?.Name ?? "");
int newIndex = EditorGUILayout.Popup("Select Type", currentIndex, typeNames);
if (newIndex != currentIndex && newIndex >= 0)
{
selectedEffectType = effectTypes[typeNames[newIndex]];
effectData.typeSpecificData.Clear();
}
}
private void DrawEffectBasicFields()
{
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Basic Settings", EditorStyles.boldLabel);
effectData.name = EditorGUILayout.TextField("Name", effectData.name);
effectData.description = EditorGUILayout.TextArea(effectData.description, GUILayout.Height(60));
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Target Settings", EditorStyles.boldLabel);
effectData.applyToTargetsHit = EditorGUILayout.Toggle("Apply To Targets Hit", effectData.applyToTargetsHit);
effectData.applyToSelf = EditorGUILayout.Toggle("Apply To Self", effectData.applyToSelf);
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Duration Settings", EditorStyles.boldLabel);
effectData.duration = EditorGUILayout.FloatField("Duration", effectData.duration);
// Draw tags
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Tags", EditorStyles.boldLabel);
DrawTagsList(effectData.tags);
}
private void DrawTagsList(List<GameTag> tags)
{
for (int i = 0; i < tags.Count; i++)
{
EditorGUILayout.BeginHorizontal();
tags[i] = (GameTag)EditorGUILayout.ObjectField(tags[i], typeof(GameTag), false);
if (GUILayout.Button("Remove", GUILayout.Width(60)))
{
tags.RemoveAt(i);
break;
}
EditorGUILayout.EndHorizontal();
}
if (GUILayout.Button("Add Tag"))
{
tags.Add(null);
}
}
private void DrawEffectTypeSpecificFields()
{
if (selectedEffectType == null) return;
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Type Specific Settings", EditorStyles.boldLabel);
if (selectedEffectType == typeof(InstantValueEffect))
{
DrawInstantValueEffectFields();
}
else if (selectedEffectType == typeof(DamageOverTimeEffect))
{
DrawDamageOverTimeEffectFields();
}
else if (selectedEffectType == typeof(StatusEffect))
{
DrawStatusEffectFields();
}
}
private void DrawInstantValueEffectFields()
{
float baseValue = effectData.GetOrCreateTypeSpecific<float>("baseValue");
baseValue = EditorGUILayout.FloatField("Base Value", baseValue);
effectData.typeSpecificData["baseValue"] = baseValue;
DamageType damageType = effectData.GetOrCreateTypeSpecific<DamageType>("damageType");
damageType = (DamageType)EditorGUILayout.EnumPopup("Damage Type", damageType);
effectData.typeSpecificData["damageType"] = damageType;
float alliedMultiplier = effectData.GetOrCreateTypeSpecific<float>("alliedMultiplier");
alliedMultiplier = EditorGUILayout.FloatField("Allied Target Multiplier", alliedMultiplier);
effectData.typeSpecificData["alliedMultiplier"] = alliedMultiplier;
float enemyMultiplier = effectData.GetOrCreateTypeSpecific<float>("enemyMultiplier");
enemyMultiplier = EditorGUILayout.FloatField("Enemy Target Multiplier", enemyMultiplier);
effectData.typeSpecificData["enemyMultiplier"] = enemyMultiplier;
}
private void DrawDamageOverTimeEffectFields()
{
float damagePerTick = effectData.GetOrCreateTypeSpecific<float>("damagePerTick");
damagePerTick = EditorGUILayout.FloatField("Damage Per Tick", damagePerTick);
effectData.typeSpecificData["damagePerTick"] = damagePerTick;
float tickRate = effectData.GetOrCreateTypeSpecific<float>("tickRate");
tickRate = EditorGUILayout.FloatField("Tick Rate", tickRate);
effectData.typeSpecificData["tickRate"] = tickRate;
DamageOverTimeType dotType = effectData.GetOrCreateTypeSpecific<DamageOverTimeType>("dotType");
dotType = (DamageOverTimeType)EditorGUILayout.EnumPopup("DoT Type", dotType);
effectData.typeSpecificData["dotType"] = dotType;
}
private void DrawStatusEffectFields()
{
EditorGUILayout.LabelField("Stat Influences", EditorStyles.boldLabel);
DrawStatInfluencesList(effectData.GetOrCreateTypeSpecific<List<StatInfluence>>("statInfluences"));
}
private void DrawStatInfluencesList(List<StatInfluence> influences)
{
for (int i = 0; i < influences.Count; i++)
{
EditorGUILayout.BeginVertical("box");
influences[i].statTag = (GameTag)EditorGUILayout.ObjectField("Stat Tag",
influences[i].statTag, typeof(GameTag), false);
influences[i].percentInfluence = EditorGUILayout.FloatField("Percent Influence",
influences[i].percentInfluence);
if (GUILayout.Button("Remove Influence"))
{
influences.RemoveAt(i);
break;
}
EditorGUILayout.EndVertical();
}
if (GUILayout.Button("Add Stat Influence"))
{
influences.Add(new StatInfluence());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c4d5e56b5f398a45923b42f9b343627
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,143 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;
public partial class AbilityEffectEditorWindow
{
private void DrawExistingEffects()
{
EditorGUILayout.LabelField("Existing Effects", EditorStyles.boldLabel);
var effects = GetAllEffects();
foreach (var effect in effects)
{
if (string.IsNullOrEmpty(searchQuery) || effect.name.ToLower().Contains(searchQuery.ToLower()))
{
EditorGUILayout.BeginVertical("box");
DrawEffectItem(effect);
EditorGUILayout.EndVertical();
}
}
}
private void DrawEffectItem(BaseEffect effect)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(effect.name, EditorStyles.boldLabel);
EditorGUILayout.LabelField($"Type: {effect.GetType().Name}");
EditorGUILayout.EndVertical();
if (GUILayout.Button("Clone", GUILayout.Width(50)))
{
CloneEffect(effect);
}
if (GUILayout.Button("Edit", GUILayout.Width(50)))
{
Selection.activeObject = effect;
EditorUtility.FocusProjectWindow();
}
EditorGUILayout.EndHorizontal();
}
private void CreateEffect()
{
if (selectedEffectType == null)
{
EditorUtility.DisplayDialog("Error", "Please select an effect type!", "OK");
return;
}
if (string.IsNullOrEmpty(effectData.name))
{
EditorUtility.DisplayDialog("Error", "Effect name cannot be empty!", "OK");
return;
}
BaseEffect effect = (BaseEffect)CreateInstance(selectedEffectType);
effect.name = effectData.name;
effect.tags = new List<GameTag>(effectData.tags);
// Apply type-specific data
ApplyEffectTypeSpecificData(effect);
string path = EditorUtility.SaveFilePanelInProject(
"Save Effect",
effectData.name,
"asset",
"Save effect asset"
);
if (!string.IsNullOrEmpty(path))
{
AssetDatabase.CreateAsset(effect, path);
AssetDatabase.SaveAssets();
EditorUtility.FocusProjectWindow();
Selection.activeObject = effect;
effectData = new EffectCreationData();
selectedEffectType = null;
}
}
private void CloneEffect(BaseEffect sourceEffect)
{
string newName = $"{sourceEffect.name}_Clone";
string path = EditorUtility.SaveFilePanelInProject(
"Save Cloned Effect",
newName,
"asset",
"Save cloned effect asset"
);
if (string.IsNullOrEmpty(path)) return;
BaseEffect newEffect = (BaseEffect)CreateInstance(sourceEffect.GetType());
EditorUtility.CopySerialized(sourceEffect, newEffect);
newEffect.name = newName;
AssetDatabase.CreateAsset(newEffect, path);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.FocusProjectWindow();
Selection.activeObject = newEffect;
}
private void ApplyEffectTypeSpecificData(BaseEffect effect)
{
if (effect is InstantValueEffect instantEffect)
{
instantEffect.baseValue = effectData.GetOrCreateTypeSpecific<float>("baseValue");
instantEffect.damageType = effectData.GetOrCreateTypeSpecific<DamageType>("damageType");
instantEffect.AlliedTargetMultiplier = effectData.GetOrCreateTypeSpecific<float>("alliedMultiplier");
instantEffect.EnemyTargetMultiplier = effectData.GetOrCreateTypeSpecific<float>("enemyMultiplier");
}
else if (effect is DamageOverTimeEffect dotEffect)
{
dotEffect.baseDamagePerTick = effectData.GetOrCreateTypeSpecific<float>("damagePerTick");
dotEffect.tickRate = effectData.GetOrCreateTypeSpecific<float>("tickRate");
dotEffect.damageType = effectData.GetOrCreateTypeSpecific<DamageOverTimeType>("dotType");
}
else if (effect is StatusEffect statusEffect)
{
statusEffect.duration = effectData.duration;
statusEffect.applyToTargetsHit = effectData.applyToTargetsHit;
statusEffect.applyToSelf = effectData.applyToSelf;
List<StatInfluence> influences = effectData.GetOrCreateTypeSpecific<List<StatInfluence>>("statInfluences");
statusEffect.influencingStats = new List<StatInfluence>(influences);
}
}
private BaseEffect[] GetAllEffects()
{
return AssetDatabase.FindAssets("t:BaseEffect")
.Select(guid => AssetDatabase.LoadAssetAtPath<BaseEffect>(AssetDatabase.GUIDToAssetPath(guid)))
.ToArray();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 586b6b091b57739439fb41c268b11672
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -94,7 +94,7 @@ Material:
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _Rotation: 284.16022
- _Rotation: 433.6615
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1