From 6ba55cb483c2bb2c98093b8bf99785127c4ab732 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 4 Dec 2024 19:22:51 +0000 Subject: [PATCH] Ability - Effect Editor window --- .../Data/Resources/Awakenings.meta | 8 + .../Awakenings/TestingAwakenings.asset | 44 ++ .../Awakenings/TestingAwakenings.asset.meta | 8 + Assets/Scripts/AbilitySystem/AbilityData.meta | 8 + .../AbilityData/BaseAbilityData.cs | 20 + .../AbilityData/BaseAbilityData.cs.meta | 11 + .../AbilitySystem/AbilityInstance.meta | 8 + .../AbilityInstance/BaseAbilityInstance.cs | 9 + .../BaseAbilityInstance.cs.meta | 11 + .../Effects/InstantValueEffect.cs | 2 - Assets/Scripts/AbilityTalentSystem.meta | 8 + .../Scripts/AbilityTalentSystem/Awakening.cs | 134 ++++++ .../AbilityTalentSystem/Awakening.cs.meta | 11 + .../AbilityTalentSystem/Awakenings.meta | 8 + .../SpecificDamageIncreaseAwakening.cs | 18 + .../SpecificDamageIncreaseAwakening.cs.meta | 11 + .../AbilityTalentSystem/ClassAwakenings.cs | 9 + .../ClassAwakenings.cs.meta | 11 + .../AbilityTalentSystem/PlayerAwakenings.cs | 34 ++ .../PlayerAwakenings.cs.meta | 11 + Assets/Scripts/Editor/ability-editor-core.cs | 70 +++ .../Editor/ability-editor-core.cs.meta | 11 + .../Scripts/Editor/ability-editor-creation.cs | 133 ++++++ .../Editor/ability-editor-creation.cs.meta | 11 + Assets/Scripts/Editor/ability-editor-data.cs | 58 +++ .../Editor/ability-editor-data.cs.meta | 11 + .../Scripts/Editor/ability-editor-existing.cs | 149 +++++++ .../Editor/ability-editor-existing.cs.meta | 11 + .../Scripts/Editor/ability-effect-editor.cs | 420 ++++++++++++++++++ .../Editor/ability-effect-editor.cs.meta | 11 + .../Scripts/Editor/effect-editor-creation.cs | 177 ++++++++ .../Editor/effect-editor-creation.cs.meta | 11 + .../Scripts/Editor/effect-editor-existing.cs | 143 ++++++ .../Editor/effect-editor-existing.cs.meta | 11 + Assets/Starfield Skybox/Skybox.mat | 2 +- 35 files changed, 1610 insertions(+), 3 deletions(-) create mode 100644 Assets/Scriptables/Data/Resources/Awakenings.meta create mode 100644 Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset create mode 100644 Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset.meta create mode 100644 Assets/Scripts/AbilitySystem/AbilityData.meta create mode 100644 Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs create mode 100644 Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs.meta create mode 100644 Assets/Scripts/AbilitySystem/AbilityInstance.meta create mode 100644 Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs create mode 100644 Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs.meta create mode 100644 Assets/Scripts/AbilityTalentSystem.meta create mode 100644 Assets/Scripts/AbilityTalentSystem/Awakening.cs create mode 100644 Assets/Scripts/AbilityTalentSystem/Awakening.cs.meta create mode 100644 Assets/Scripts/AbilityTalentSystem/Awakenings.meta create mode 100644 Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs create mode 100644 Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs.meta create mode 100644 Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs create mode 100644 Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs.meta create mode 100644 Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs create mode 100644 Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs.meta create mode 100644 Assets/Scripts/Editor/ability-editor-core.cs create mode 100644 Assets/Scripts/Editor/ability-editor-core.cs.meta create mode 100644 Assets/Scripts/Editor/ability-editor-creation.cs create mode 100644 Assets/Scripts/Editor/ability-editor-creation.cs.meta create mode 100644 Assets/Scripts/Editor/ability-editor-data.cs create mode 100644 Assets/Scripts/Editor/ability-editor-data.cs.meta create mode 100644 Assets/Scripts/Editor/ability-editor-existing.cs create mode 100644 Assets/Scripts/Editor/ability-editor-existing.cs.meta create mode 100644 Assets/Scripts/Editor/ability-effect-editor.cs create mode 100644 Assets/Scripts/Editor/ability-effect-editor.cs.meta create mode 100644 Assets/Scripts/Editor/effect-editor-creation.cs create mode 100644 Assets/Scripts/Editor/effect-editor-creation.cs.meta create mode 100644 Assets/Scripts/Editor/effect-editor-existing.cs create mode 100644 Assets/Scripts/Editor/effect-editor-existing.cs.meta diff --git a/Assets/Scriptables/Data/Resources/Awakenings.meta b/Assets/Scriptables/Data/Resources/Awakenings.meta new file mode 100644 index 00000000..b4b5669b --- /dev/null +++ b/Assets/Scriptables/Data/Resources/Awakenings.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e0c2df9140a6fce45a9576a1db3e0d43 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset b/Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset new file mode 100644 index 00000000..0d1af415 --- /dev/null +++ b/Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset @@ -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 diff --git a/Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset.meta b/Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset.meta new file mode 100644 index 00000000..a810da2b --- /dev/null +++ b/Assets/Scriptables/Data/Resources/Awakenings/TestingAwakenings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bfc36a68112b4534c8eb33a78518ab6c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilitySystem/AbilityData.meta b/Assets/Scripts/AbilitySystem/AbilityData.meta new file mode 100644 index 00000000..775cb8e8 --- /dev/null +++ b/Assets/Scripts/AbilitySystem/AbilityData.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 307841cd77271a8468c442f4a604e862 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs b/Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs new file mode 100644 index 00000000..f91d5ad7 --- /dev/null +++ b/Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs @@ -0,0 +1,20 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +[System.Serializable] +public class BaseAbilityData +{ + public Sprite Icon; + public List targettingTags = new List(); + public List tags = new List(); + public List abilityEffects = new List(); + [Space] + public float castTime; + public float manaCost; + public float healthCost = 0; + public float classResourceCost = 0; + public float cooldown; + public bool castableWhileMoving; + public AbilityAnimationType animationType; +} diff --git a/Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs.meta b/Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs.meta new file mode 100644 index 00000000..f45d5302 --- /dev/null +++ b/Assets/Scripts/AbilitySystem/AbilityData/BaseAbilityData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85e3596f5bd1bf44d8343b3f2b4cd7ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilitySystem/AbilityInstance.meta b/Assets/Scripts/AbilitySystem/AbilityInstance.meta new file mode 100644 index 00000000..7b773e9d --- /dev/null +++ b/Assets/Scripts/AbilitySystem/AbilityInstance.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9b2dc875bac316e49b4963b666aafd44 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs b/Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs new file mode 100644 index 00000000..e0ddc128 --- /dev/null +++ b/Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs @@ -0,0 +1,9 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +[System.Serializable] +public class BaseAbilityInstance +{ + public BaseAbilityData abilityData; +} diff --git a/Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs.meta b/Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs.meta new file mode 100644 index 00000000..bdad3d02 --- /dev/null +++ b/Assets/Scripts/AbilitySystem/AbilityInstance/BaseAbilityInstance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ecbc603708f7b784ab0a4bc2cce24156 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilitySystem/Effects/InstantValueEffect.cs b/Assets/Scripts/AbilitySystem/Effects/InstantValueEffect.cs index ad7b3e3b..013a859e 100644 --- a/Assets/Scripts/AbilitySystem/Effects/InstantValueEffect.cs +++ b/Assets/Scripts/AbilitySystem/Effects/InstantValueEffect.cs @@ -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; diff --git a/Assets/Scripts/AbilityTalentSystem.meta b/Assets/Scripts/AbilityTalentSystem.meta new file mode 100644 index 00000000..a39ca537 --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b3e9fe72bbbe46648920a316a683101f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilityTalentSystem/Awakening.cs b/Assets/Scripts/AbilityTalentSystem/Awakening.cs new file mode 100644 index 00000000..c4c863fd --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/Awakening.cs @@ -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 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 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 requiredAwakenings = new List(); + + [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(); + } + public AwakeningData(string name, string description) + { + Name = name; + Description = description; + CurrentPoints = 0; + MaxPoints = 1; + requiredAwakenings = new List(); + } + public AwakeningData(string name, string description, int currentPoints, int totalPoints, List 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; + } +} \ No newline at end of file diff --git a/Assets/Scripts/AbilityTalentSystem/Awakening.cs.meta b/Assets/Scripts/AbilityTalentSystem/Awakening.cs.meta new file mode 100644 index 00000000..86ca44be --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/Awakening.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03064a0d3f6200145b50c40e4cd12615 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilityTalentSystem/Awakenings.meta b/Assets/Scripts/AbilityTalentSystem/Awakenings.meta new file mode 100644 index 00000000..22a51b41 --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/Awakenings.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bac7dcb65e711114fa3203ffde968f74 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs b/Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs new file mode 100644 index 00000000..ccd24a89 --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs @@ -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() + { + + } +} diff --git a/Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs.meta b/Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs.meta new file mode 100644 index 00000000..99bd5301 --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/Awakenings/SpecificDamageIncreaseAwakening.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12323d49914f700448c37231a033bb2c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs b/Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs new file mode 100644 index 00000000..0e5a938c --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs @@ -0,0 +1,9 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ClassAwakenings +{ + public List AllAvailableAwakenings = new List(); + +} diff --git a/Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs.meta b/Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs.meta new file mode 100644 index 00000000..982fe4b1 --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/ClassAwakenings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9485a30ee3b5f804d959e444e9d5130c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs b/Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs new file mode 100644 index 00000000..9db91a06 --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs @@ -0,0 +1,34 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class PlayerAwakenings : MonoBehaviour +{ + private List unlockedAbilities = new List(); + private List unlockedAwakenings = new List(); + + 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); + } +} diff --git a/Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs.meta b/Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs.meta new file mode 100644 index 00000000..a4858561 --- /dev/null +++ b/Assets/Scripts/AbilityTalentSystem/PlayerAwakenings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a860d52346f7abb4fbc40f3477364ae9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/ability-editor-core.cs b/Assets/Scripts/Editor/ability-editor-core.cs new file mode 100644 index 00000000..d6a31584 --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-core.cs @@ -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 abilityTypes; + + [MenuItem("Tools/Ability Editor")] + public static void ShowWindow() + { + GetWindow("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(); + } +} diff --git a/Assets/Scripts/Editor/ability-editor-core.cs.meta b/Assets/Scripts/Editor/ability-editor-core.cs.meta new file mode 100644 index 00000000..ce5bd9f8 --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-core.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 75d10d37171d2d24d8d4069eba82bbe3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/ability-editor-creation.cs b/Assets/Scripts/Editor/ability-editor-creation.cs new file mode 100644 index 00000000..1cc0dbfd --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-creation.cs @@ -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("projectileSpeed"); + projectileSpeed = EditorGUILayout.FloatField("Projectile Speed", projectileSpeed); + abilityData.typeSpecificData["projectileSpeed"] = projectileSpeed; + + float lifeSpan = abilityData.GetOrCreateTypeSpecific("lifeSpan"); + lifeSpan = EditorGUILayout.FloatField("Life Span", lifeSpan); + abilityData.typeSpecificData["lifeSpan"] = lifeSpan; + + bool canPierce = abilityData.GetOrCreateTypeSpecific("canPierce"); + canPierce = EditorGUILayout.Toggle("Can Pierce", canPierce); + abilityData.typeSpecificData["canPierce"] = canPierce; + + GameObject projectilePrefab = abilityData.GetOrCreateTypeSpecific("projectilePrefab"); + projectilePrefab = (GameObject)EditorGUILayout.ObjectField("Projectile Prefab", + projectilePrefab, typeof(GameObject), false); + abilityData.typeSpecificData["projectilePrefab"] = projectilePrefab; + } + + private void DrawAreaOfEffectFields() + { + float radius = abilityData.GetOrCreateTypeSpecific("radius"); + radius = EditorGUILayout.FloatField("Radius", radius); + abilityData.typeSpecificData["radius"] = radius; + + float duration = abilityData.GetOrCreateTypeSpecific("duration"); + duration = EditorGUILayout.FloatField("Duration", duration); + abilityData.typeSpecificData["duration"] = duration; + + GameObject aoePrefab = abilityData.GetOrCreateTypeSpecific("aoePrefab"); + aoePrefab = (GameObject)EditorGUILayout.ObjectField("AoE Prefab", + aoePrefab, typeof(GameObject), false); + abilityData.typeSpecificData["aoePrefab"] = aoePrefab; + } + + private void DrawChanneledFields() + { + float channelDuration = abilityData.GetOrCreateTypeSpecific("channelDuration"); + channelDuration = EditorGUILayout.FloatField("Channel Duration", channelDuration); + abilityData.typeSpecificData["channelDuration"] = channelDuration; + + bool canMove = abilityData.GetOrCreateTypeSpecific("canMove"); + canMove = EditorGUILayout.Toggle("Can Move While Channeling", canMove); + abilityData.typeSpecificData["canMove"] = canMove; + } +} diff --git a/Assets/Scripts/Editor/ability-editor-creation.cs.meta b/Assets/Scripts/Editor/ability-editor-creation.cs.meta new file mode 100644 index 00000000..11043de2 --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-creation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86bffbad8290be14f98db3d5ac63a370 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/ability-editor-data.cs b/Assets/Scripts/Editor/ability-editor-data.cs new file mode 100644 index 00000000..c6d1188a --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-data.cs @@ -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 typeSpecificData = new SerializedDictionary(); + + public T GetOrCreateTypeSpecific(string key) + { + if (!typeSpecificData.ContainsKey(key)) + { + typeSpecificData[key] = default(T); + } + return (T)typeSpecificData[key]; + } +} + +[System.Serializable] +public class SerializedDictionary : Dictionary, ISerializationCallbackReceiver +{ + [SerializeField] + private List keys = new List(); + + [SerializeField] + private List values = new List(); + + public void OnBeforeSerialize() + { + keys.Clear(); + values.Clear(); + foreach(KeyValuePair 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]); + } +} diff --git a/Assets/Scripts/Editor/ability-editor-data.cs.meta b/Assets/Scripts/Editor/ability-editor-data.cs.meta new file mode 100644 index 00000000..15388dfd --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-data.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 36c5f08218aeb2c44b191463947d6160 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/ability-editor-existing.cs b/Assets/Scripts/Editor/ability-editor-existing.cs new file mode 100644 index 00000000..5dd5256d --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-existing.cs @@ -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("projectileSpeed"); + projectileAbility.lifeSpan = abilityData.GetOrCreateTypeSpecific("lifeSpan"); + projectileAbility.canPierce = abilityData.GetOrCreateTypeSpecific("canPierce"); + projectileAbility.projectilePrefab = abilityData.GetOrCreateTypeSpecific("projectilePrefab"); + } + else if (ability is AreaOfEffectAbility aoeAbility) + { + aoeAbility.radius = abilityData.GetOrCreateTypeSpecific("radius"); + aoeAbility.lifeSpan = abilityData.GetOrCreateTypeSpecific("duration"); + aoeAbility.aoePrefab = abilityData.GetOrCreateTypeSpecific("aoePrefab"); + } + else if (ability is ChanneledAbility channeledAbility) + { + channeledAbility.duration = abilityData.GetOrCreateTypeSpecific("channelDuration"); + channeledAbility.castableWhileMoving = abilityData.GetOrCreateTypeSpecific("canMove"); + } + } + + private BaseAbility[] GetAllAbilities() + { + return AssetDatabase.FindAssets("t:BaseAbility") + .Select(guid => AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid))) + .ToArray(); + } +} diff --git a/Assets/Scripts/Editor/ability-editor-existing.cs.meta b/Assets/Scripts/Editor/ability-editor-existing.cs.meta new file mode 100644 index 00000000..b73c22ea --- /dev/null +++ b/Assets/Scripts/Editor/ability-editor-existing.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a19e286df3408fa4f9e2f82a2d93d2b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/ability-effect-editor.cs b/Assets/Scripts/Editor/ability-effect-editor.cs new file mode 100644 index 00000000..15bf21cd --- /dev/null +++ b/Assets/Scripts/Editor/ability-effect-editor.cs @@ -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 abilityTypes; + private Dictionary effectTypes; + + // Tabs + private int selectedTab = 0; + private readonly string[] tabNames = { "Abilities", "Effects" }; + + [MenuItem("Tools/Ability & Effect Editor")] + public static void ShowWindow() + { + GetWindow("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("projectileSpeed"); + projectileSpeed = EditorGUILayout.FloatField("Projectile Speed", projectileSpeed); + abilityData.typeSpecificData["projectileSpeed"] = projectileSpeed; + + float lifeSpan = abilityData.GetOrCreateTypeSpecific("lifeSpan"); + lifeSpan = EditorGUILayout.FloatField("Life Span", lifeSpan); + abilityData.typeSpecificData["lifeSpan"] = lifeSpan; + + bool canPierce = abilityData.GetOrCreateTypeSpecific("canPierce"); + canPierce = EditorGUILayout.Toggle("Can Pierce", canPierce); + abilityData.typeSpecificData["canPierce"] = canPierce; + + GameObject projectilePrefab = abilityData.GetOrCreateTypeSpecific("projectilePrefab"); + projectilePrefab = (GameObject)EditorGUILayout.ObjectField("Projectile Prefab", + projectilePrefab, typeof(GameObject), false); + abilityData.typeSpecificData["projectilePrefab"] = projectilePrefab; + } + + private void DrawAreaOfEffectFields() + { + float radius = abilityData.GetOrCreateTypeSpecific("radius"); + radius = EditorGUILayout.FloatField("Radius", radius); + abilityData.typeSpecificData["radius"] = radius; + + float duration = abilityData.GetOrCreateTypeSpecific("duration"); + duration = EditorGUILayout.FloatField("Duration", duration); + abilityData.typeSpecificData["duration"] = duration; + + GameObject aoePrefab = abilityData.GetOrCreateTypeSpecific("aoePrefab"); + aoePrefab = (GameObject)EditorGUILayout.ObjectField("AoE Prefab", + aoePrefab, typeof(GameObject), false); + abilityData.typeSpecificData["aoePrefab"] = aoePrefab; + } + + private void DrawChanneledFields() + { + float channelDuration = abilityData.GetOrCreateTypeSpecific("channelDuration"); + channelDuration = EditorGUILayout.FloatField("Channel Duration", channelDuration); + abilityData.typeSpecificData["channelDuration"] = channelDuration; + + bool canMove = abilityData.GetOrCreateTypeSpecific("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("projectileSpeed"); + projectileAbility.lifeSpan = abilityData.GetOrCreateTypeSpecific("lifeSpan"); + projectileAbility.canPierce = abilityData.GetOrCreateTypeSpecific("canPierce"); + projectileAbility.projectilePrefab = abilityData.GetOrCreateTypeSpecific("projectilePrefab"); + } + else if (ability is AreaOfEffectAbility aoeAbility) + { + aoeAbility.radius = abilityData.GetOrCreateTypeSpecific("radius"); + aoeAbility.lifeSpan = abilityData.GetOrCreateTypeSpecific("duration"); + aoeAbility.aoePrefab = abilityData.GetOrCreateTypeSpecific("aoePrefab"); + } + else if (ability is ChanneledAbility channeledAbility) + { + channeledAbility.duration = abilityData.GetOrCreateTypeSpecific("channelDuration"); + channeledAbility.castableWhileMoving = abilityData.GetOrCreateTypeSpecific("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(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 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 tags = new List(); + public List statInfluences = new List(); + public SerializedDictionary typeSpecificData = new SerializedDictionary(); + + public T GetOrCreateTypeSpecific(string key) + { + if (!typeSpecificData.ContainsKey(key)) + { + if (typeof(T) == typeof(List)) + typeSpecificData[key] = new List(); + else + typeSpecificData[key] = default(T); + } + return (T)typeSpecificData[key]; + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/ability-effect-editor.cs.meta b/Assets/Scripts/Editor/ability-effect-editor.cs.meta new file mode 100644 index 00000000..886c0580 --- /dev/null +++ b/Assets/Scripts/Editor/ability-effect-editor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 55d583c683871fd47ad6807ca5ce8edd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/effect-editor-creation.cs b/Assets/Scripts/Editor/effect-editor-creation.cs new file mode 100644 index 00000000..45de9c3e --- /dev/null +++ b/Assets/Scripts/Editor/effect-editor-creation.cs @@ -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 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("baseValue"); + baseValue = EditorGUILayout.FloatField("Base Value", baseValue); + effectData.typeSpecificData["baseValue"] = baseValue; + + DamageType damageType = effectData.GetOrCreateTypeSpecific("damageType"); + damageType = (DamageType)EditorGUILayout.EnumPopup("Damage Type", damageType); + effectData.typeSpecificData["damageType"] = damageType; + + float alliedMultiplier = effectData.GetOrCreateTypeSpecific("alliedMultiplier"); + alliedMultiplier = EditorGUILayout.FloatField("Allied Target Multiplier", alliedMultiplier); + effectData.typeSpecificData["alliedMultiplier"] = alliedMultiplier; + + float enemyMultiplier = effectData.GetOrCreateTypeSpecific("enemyMultiplier"); + enemyMultiplier = EditorGUILayout.FloatField("Enemy Target Multiplier", enemyMultiplier); + effectData.typeSpecificData["enemyMultiplier"] = enemyMultiplier; + } + + private void DrawDamageOverTimeEffectFields() + { + float damagePerTick = effectData.GetOrCreateTypeSpecific("damagePerTick"); + damagePerTick = EditorGUILayout.FloatField("Damage Per Tick", damagePerTick); + effectData.typeSpecificData["damagePerTick"] = damagePerTick; + + float tickRate = effectData.GetOrCreateTypeSpecific("tickRate"); + tickRate = EditorGUILayout.FloatField("Tick Rate", tickRate); + effectData.typeSpecificData["tickRate"] = tickRate; + + DamageOverTimeType dotType = effectData.GetOrCreateTypeSpecific("dotType"); + dotType = (DamageOverTimeType)EditorGUILayout.EnumPopup("DoT Type", dotType); + effectData.typeSpecificData["dotType"] = dotType; + } + + private void DrawStatusEffectFields() + { + EditorGUILayout.LabelField("Stat Influences", EditorStyles.boldLabel); + DrawStatInfluencesList(effectData.GetOrCreateTypeSpecific>("statInfluences")); + } + + private void DrawStatInfluencesList(List 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()); + } + } +} diff --git a/Assets/Scripts/Editor/effect-editor-creation.cs.meta b/Assets/Scripts/Editor/effect-editor-creation.cs.meta new file mode 100644 index 00000000..97a987e4 --- /dev/null +++ b/Assets/Scripts/Editor/effect-editor-creation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c4d5e56b5f398a45923b42f9b343627 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Editor/effect-editor-existing.cs b/Assets/Scripts/Editor/effect-editor-existing.cs new file mode 100644 index 00000000..58735b05 --- /dev/null +++ b/Assets/Scripts/Editor/effect-editor-existing.cs @@ -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(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("baseValue"); + instantEffect.damageType = effectData.GetOrCreateTypeSpecific("damageType"); + instantEffect.AlliedTargetMultiplier = effectData.GetOrCreateTypeSpecific("alliedMultiplier"); + instantEffect.EnemyTargetMultiplier = effectData.GetOrCreateTypeSpecific("enemyMultiplier"); + } + else if (effect is DamageOverTimeEffect dotEffect) + { + dotEffect.baseDamagePerTick = effectData.GetOrCreateTypeSpecific("damagePerTick"); + dotEffect.tickRate = effectData.GetOrCreateTypeSpecific("tickRate"); + dotEffect.damageType = effectData.GetOrCreateTypeSpecific("dotType"); + } + else if (effect is StatusEffect statusEffect) + { + statusEffect.duration = effectData.duration; + statusEffect.applyToTargetsHit = effectData.applyToTargetsHit; + statusEffect.applyToSelf = effectData.applyToSelf; + + List influences = effectData.GetOrCreateTypeSpecific>("statInfluences"); + statusEffect.influencingStats = new List(influences); + } + } + + private BaseEffect[] GetAllEffects() + { + return AssetDatabase.FindAssets("t:BaseEffect") + .Select(guid => AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid))) + .ToArray(); + } +} diff --git a/Assets/Scripts/Editor/effect-editor-existing.cs.meta b/Assets/Scripts/Editor/effect-editor-existing.cs.meta new file mode 100644 index 00000000..6b3e8221 --- /dev/null +++ b/Assets/Scripts/Editor/effect-editor-existing.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 586b6b091b57739439fb41c268b11672 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Starfield Skybox/Skybox.mat b/Assets/Starfield Skybox/Skybox.mat index e04ccc08..74930b38 100644 --- a/Assets/Starfield Skybox/Skybox.mat +++ b/Assets/Starfield Skybox/Skybox.mat @@ -94,7 +94,7 @@ Material: - _Mode: 0 - _OcclusionStrength: 1 - _Parallax: 0.02 - - _Rotation: 284.16022 + - _Rotation: 433.6615 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 - _SrcBlend: 1