[HorizontalGroup("Info", Width = 325)]
[VerticalGroup("Info/Side1", Order =2), LabelWidth(50)]
public string id;
[VerticalGroup("Info/Side1"), LabelWidth(50)]
public string title;
[VerticalGroup("Info/Side2"), PreviewField(100), HideLabel,]
public Sprite art_full;
[VerticalGroup("Info/Side2"), PreviewField(100), HideLabel,]
public Sprite art_board;
[VerticalGroup("Info/Side1"), LabelWidth(50)]
public CardType type;
[VerticalGroup("Info/Side1"), LabelWidth(50)]
[InlineButton("@CardEditor.CreateNewAction($root, $property)", SdfIconType.PlusCircleDotted, "")]
public TeamData team;
[VerticalGroup("Info/Side1"), LabelWidth(50)]
[InlineButton("@CardEditor.CreateNewAction($root, $property)", SdfIconType.PlusCircleDotted, "")]
public RarityData rarity;
[HorizontalGroup("Info/Side1/Stats"), LabelWidth(30), LabelText(SdfIconType.DiamondFill, Text = "")]
public int mana;
[HorizontalGroup("Info/Side1/Stats"), LabelWidth(30), LabelText(SdfIconType.Hammer, Text = "")]
public int attack;
[HorizontalGroup("Info/Side1/Stats"), LabelWidth(30), LabelText(SdfIconType.DropletFill, Text = "")]
public int hp;
[TabGroup("Traits", Icon = SdfIconType.Speedometer), ListDrawerSettings]
public TraitData[] traits;
[TabGroup("Traits", Icon = SdfIconType.Speedometer)]
public TraitStat[] stats;
[TabGroup("Abilities", Icon = SdfIconType.Magic), ListDrawerSettings]
public AbilityData[] abilities;
[VerticalGroup("Info/Side1"), LabelWidth(50)]
[TextArea(3, 5)]
public string text;
[VerticalGroup("Info/Side1"), LabelWidth(50)]
[TextArea(5, 10)]
public string desc;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject spawn_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject death_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject attack_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject damage_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject idle_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public AudioClip spawn_audio;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public AudioClip death_audio;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public AudioClip attack_audio;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public AudioClip damage_audio;
[TabGroup("FX", Icon = SdfIconType.Stars)]
[TabGroup("Availability", Icon = SdfIconType.Basket3Fill)]
public bool deckbuilding = false;
[TabGroup("Availability", Icon = SdfIconType.Basket3Fill)]
public int cost = 100;
[TabGroup("Availability", Icon = SdfIconType.Basket3Fill)]
public PackData[] packs;
//...
[InlineButton("@CardEditor.CloneAction($root, $property, $value)", SdfIconType.FileEarmarkPlusFill, "", ShowIf = "@CardEditor.ShowNotNull($value)")]
public class AbilityData : ScriptableObject
{
public string id;
[TabGroup("Trigger", Icon = SdfIconType.Alarm), EnumToggleButtons, HideLabel]
public AbilityTrigger trigger; //WHEN does the ability trigger?
[TabGroup("Trigger", Icon = SdfIconType.Alarm), ListDrawerSettings]
public ConditionData[] conditions_trigger; //Condition checked on the card triggering the ability (usually the caster)
[TabGroup("Target", Icon = SdfIconType.Capslock), EnumToggleButtons, HideLabel]
public AbilityTarget target; //WHO is targeted?
[TabGroup("Target", Icon = SdfIconType.Capslock), ListDrawerSettings]
public ConditionData[] conditions_target; //Condition checked on the target to know if its a valid taget
[TabGroup("Target", Icon = SdfIconType.Capslock), ListDrawerSettings]
public FilterData[] filters_target; //Condition checked on the target to know if its a valid taget
[TabGroup("Effect", Icon = SdfIconType.Lightning), ListDrawerSettings]
public EffectData[] effects; //WHAT this does?
[TabGroup("Effect", Icon = SdfIconType.Lightning), ListDrawerSettings]
public StatusData[] status; //Status added by this ability
[TabGroup("Effect", Icon = SdfIconType.Lightning)]
public int value; //Value passed to the effect (deal X damage)
[TabGroup("Effect", Icon = SdfIconType.Lightning)]
public int duration; //Duration passed to the effect (usually for status, 0=permanent)
[TabGroup("Chain or Choices", Icon = SdfIconType.SignpostSplit), ListDrawerSettings]
public AbilityData[] chain_abilities; //Abilities that will be triggered after this one
[Header("Activated Ability")]
public int mana_cost; //Mana cost for activated abilities
public bool exhaust; //Action cost for activated abilities
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject board_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject caster_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public GameObject target_fx;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public AudioClip cast_audio;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public AudioClip target_audio;
[TabGroup("FX", Icon = SdfIconType.Stars)]
public bool charge_target;
[Header("Text")]
public string title;
[TextArea(5, 7)]
public string desc;
//...
}
[InlineButton("@CardEditor.CloneAction($root, $property, $value)", SdfIconType.FileEarmarkPlusFill, "", ShowIf = "@CardEditor.ShowNotNull($value)")]
public class ConditionData : ScriptableObject
[InlineButton("@CardEditor.CloneAction($root, $property, $value)", SdfIconType.FileEarmarkPlusFill, "", ShowIf = "@CardEditor.ShowNotNull($value)")]
public class FilterData : ScriptableObject
[InlineButton("@CardEditor.CloneAction($root, $property, $value)", SdfIconType.FileEarmarkPlusFill, "", ShowIf = "@CardEditor.ShowNotNull($value)")]
public class EffectData : ScriptableObject
[InlineButton("@CardEditor.CloneAction($root, $property, $value)", SdfIconType.FileEarmarkPlusFill, "", ShowIf = "@CardEditor.ShowNotNull($value)")]
public class StatusData : ScriptableObject
[InlineButton("@CardEditor.CloneAction($root, $property, $value)", SdfIconType.FileEarmarkPlusFill, "", ShowIf = "@CardEditor.ShowNotNull($value)")]
public class TraitData : ScriptableObject
[InlineButton("@CardEditor.CloneAction($root, $property, $value)", SdfIconType.FileEarmarkPlusFill, "", ShowIf = "@CardEditor.ShowNotNull($value)")]
public class RarityData : ScriptableObject
using Sirenix.OdinInspector.Editor;
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using TcgEngine;
using Sirenix.Utilities.Editor;
using Sirenix.OdinInspector.Demos.RPGEditor;
using Sirenix.OdinInspector;
using System.Reflection;
using Sirenix.Utilities;
using Unity.VisualScripting;
using System.Linq;
public class DuplicateFinder : OdinMenuEditorWindow
{
[MenuItem("Tools/Moratelli/Duplicates Finder")]
private static void OpenEditor() => GetWindow<DuplicateFinder>();
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree();
tree.Config.DrawSearchToolbar = true;
var assets = Resources.LoadAll("")
.Where(obj => obj.GetType().GetField("id") != null)
.OrderBy(obj => obj.ToString())
.GroupBy(x => new { id = x.GetType().GetField("id").GetValue(x), type = x.GetType().Name })
.Where(group => group.Sum(x => 1) > 1);
if (assets.Count() == 0)
{
tree.Add("Everything good!", null, SdfIconType.HandThumbsUpFill);
}
else
{
tree.Add("There are duplications!", null, SdfIconType.HandThumbsDownFill);
}
foreach (var group in assets)
{
foreach (var item in group)
{
tree.Add($"{group.Key.id}/{item.name}", item);
}
}
return tree;
}
}
public class CardFinderForm
{
[VerticalGroup("Form", PaddingBottom = 50)]
public string cardName = "";
[VerticalGroup("Form")]
public AbilityData selectedAbility = null;
[VerticalGroup("Form")]
public AbilityTrigger selectedTrigger = AbilityTrigger.None;
[VerticalGroup("Form")]
public TeamData selectedTeam = null;
[VerticalGroup("Form")]
public RarityData selectedRarity = null;
[VerticalGroup("Form")]
[TextArea(3, 5)]
public string text = "";
[VerticalGroup("Form")]
public TraitData selectedTrait = null;
[VerticalGroup("Form")]
public PackData selectedPack = null;
private OdinMenuEditorWindow parent;
public CardFinderForm(OdinMenuEditorWindow parent)
{
this.parent = parent;
}
[VerticalGroup("Actions")]
[HorizontalGroup("Actions/Filter")]
[Button(ButtonSizes.Large, Icon = SdfIconType.TrashFill)]
private void ClearFilters()
{
selectedTrigger = AbilityTrigger.None;
selectedAbility = null;
cardName = "";
selectedTeam = null;
selectedRarity = null;
text = "";
selectedTrait = null;
selectedPack = null;
parent.ForceMenuTreeRebuild();
}
[HorizontalGroup("Actions/Filter")]
[Button(ButtonSizes.Large, Icon = SdfIconType.FunnelFill)]
private void Filter()
{
parent.ForceMenuTreeRebuild();
}
}
public class CardFinder : OdinMenuEditorWindow
{
[MenuItem("Tools/Moratelli/Card Finder")]
private static void OpenEditor() => GetWindow<CardFinder>();
private CardFinderForm form;
private CardData[] cardDataAssets;
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree();
form ??= new CardFinderForm(this);
tree.Config.DrawSearchToolbar = true;
tree.Add("Filters", form);
cardDataAssets = Resources.LoadAll<CardData>("").OrderBy(card => card.name).ToArray();
foreach (CardData cardData in cardDataAssets)
{
bool show = true;
if (form.selectedAbility != null && cardData.abilities.FirstOrDefault(ability => ability.id == form.selectedAbility.id) == null)
{
show = false;
}
if (form.selectedTrigger != AbilityTrigger.None && cardData.abilities.FirstOrDefault(ability => ability.trigger == form.selectedTrigger) == null)
{
show = false;
}
if (form.cardName != "" && cardData.name.ToLower().Contains(form.cardName.ToLower()) == false)
{
show = false;
}
if (form.text != "" && cardData.text.ToLower().Contains(form.text.ToLower()) == false)
{
show = false;
}
if (form.selectedPack != null && cardData.packs.FirstOrDefault(pack => pack.id == form.selectedPack.id) == null)
{
show = false;
}
if (form.selectedTeam != null && cardData.team.id != form.selectedTeam.id)
{
show = false;
}
if (form.selectedTrait != null && cardData.traits.FirstOrDefault(pack => pack.id == form.selectedTrait.id) == null)
{
show = false;
}
if (form.selectedRarity != null && cardData.rarity.id != form.selectedRarity.id)
{
show = false;
}
if (show)
{
tree.Add($"Card/{cardData.name}", cardData);
}
}
return tree;
}
protected override void OnBeginDrawEditors()
{
if (MenuTree == null) return;
SirenixEditorGUI.BeginHorizontalToolbar(MenuTree.Config.SearchToolbarHeight);
{
if (SirenixEditorGUI.ToolbarButton(new GUIContent("Clone")))
{
var selected = MenuTree.Selection.SelectedValue;
if (selected == null || !selected.GetType().IsSubclassOf(typeof(ScriptableObject))) return;
Type selectedType = selected.GetType();
var objToSelect = EditorUtils.Clone(selectedType, selected);
if (objToSelect != null)
{
TrySelectMenuItemWithObject(objToSelect);
this.ForceMenuTreeRebuild();
}
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("Rename file")))
{
var selected = MenuTree.Selection.SelectedValue as UnityEngine.Object;
var path = AssetDatabase.GetAssetPath(selected);
var propertyInfo = selected.GetType().GetField("id");
if (propertyInfo != null)
{
var id = propertyInfo.GetValue(selected) as string;
AssetDatabase.RenameAsset(path, id);
//string[] parts = AssetDatabase.GetAssetPath(selected).Split("/");
//parts[^1] = id;
//var newPath = string.Join("/", parts);
//var renamedObject = AssetDatabase.LoadAssetAtPath(newPath, selected.GetType());
//TrySelectMenuItemWithObject(renamedObject);
}
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("Delete")))
{
var selected = MenuTree.Selection.SelectedValue as UnityEngine.Object;
if (EditorUtility.DisplayDialog("Delete", $"Are you sure you want to delete \"{selected.name}\" ({selected.GetType()})?", "Yes", "No"))
{
var path = AssetDatabase.GetAssetPath(selected);
AssetDatabase.DeleteAsset(path);
}
}
}
SirenixEditorGUI.EndHorizontalToolbar();
}
}
public class ScriptableObjectFinderForm
{
private string[] types;
[ValueDropdown("types")]
[VerticalGroup("Form", PaddingBottom = 50)]
public string selectedType = "";
private OdinMenuEditorWindow parent;
public ScriptableObjectFinderForm(OdinMenuEditorWindow parent)
{
types = typeof(CardData)
.Assembly.GetTypes()
.Where(type => type.IsSubclassOf(typeof(ScriptableObject)))
.Select(type => type.ToString()).ToArray();
this.parent = parent;
}
[VerticalGroup("Actions")]
[HorizontalGroup("Actions/Filter")]
[Button(ButtonSizes.Large, Icon = SdfIconType.TrashFill)]
private void ClearFilters()
{
selectedType = "";
parent.ForceMenuTreeRebuild();
}
[HorizontalGroup("Actions/Filter")]
[Button(ButtonSizes.Large, Icon = SdfIconType.FunnelFill)]
private void Filter()
{
parent.ForceMenuTreeRebuild();
}
}
public class ScriptableObjectFinder : OdinMenuEditorWindow
{
[MenuItem("Tools/Moratelli/SO Finder")]
private static void OpenEditor() => GetWindow<ScriptableObjectFinder>();
private ScriptableObjectFinderForm form;
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree();
form ??= new ScriptableObjectFinderForm(this);
tree.Config.DrawSearchToolbar = true;
tree.Add("Filters", form);
var asm = typeof(ConditionData).Assembly;
ScriptableObject[] assets = Resources.LoadAll<ScriptableObject>("").OrderBy(obj => obj.ToString()).ToArray();
foreach (ScriptableObject asset in assets)
{
bool show = true;
if (asset.GetType().Name.Contains("TcgEngine."))
{
show = false;
}
if (form.selectedType != "")
{
var search = tree.Config.SearchTerm;
var type = asm.GetType(form.selectedType);
if (type == null)
{
show = false;
}
else
{
show = asset.GetType() == type || asset.GetType().IsSubclassOf(type);
}
}
if (show)
{
tree.Add($"{asset.GetType().Name.Split("TcgEngine.")[0]}/{asset.name}", asset);
}
}
return tree;
}
protected override void OnBeginDrawEditors()
{
if (MenuTree == null) return;
SirenixEditorGUI.BeginHorizontalToolbar(MenuTree.Config.SearchToolbarHeight);
{
if (SirenixEditorGUI.ToolbarButton(new GUIContent("Clone")))
{
var selected = MenuTree.Selection.SelectedValue;
if (selected == null || !selected.GetType().IsSubclassOf(typeof(ScriptableObject))) return;
Type selectedType = selected.GetType();
var objToSelect = EditorUtils.Clone(selectedType, selected);
if (objToSelect != null)
{
TrySelectMenuItemWithObject(objToSelect);
}
}
}
SirenixEditorGUI.EndHorizontalToolbar();
}
}
public class CardEditor : OdinMenuEditorWindow
{
[MenuItem("Tools/Moratelli/Card Manager")]
private static void OpenEditor() => GetWindow<CardEditor>();
public static bool ShowNotNull(UnityEngine.Object value)
{
return value != null;
}
public static void CloneAction<T>(object root, InspectorProperty property, T value)
{
if (value == null) return;
string fieldName;
if (property.Parent.Name.Contains("#") || property.Parent.Name.Contains("$"))
{
fieldName = property.Name;
}
else
{
fieldName = property.Parent.Name;
}
var field = root.GetType().GetField(fieldName);
if (field != null && field.FieldType.IsArray)
{
var array = field.GetValue(root) as T[];
T item = (T)EditorUtils.Clone(value.GetType(), value);
if (item != null)
{
Array.Resize(ref array, array.Length + 1);
array[^1] = item;
field.SetValue(root, array);
}
}
else if (field != null)
{
T item = (T)EditorUtils.Clone(value.GetType(), value);
if (item != null)
{
field.SetValue(root, item);
}
}
}
static public void CreateNewAction(object root, InspectorProperty property)
{
string fieldName;
if (property.Parent.Name.Contains("#") || property.Parent.Name.Contains("$"))
{
fieldName = property.Name;
}
else
{
fieldName = property.Parent.Name;
}
FieldInfo field = root.GetType().GetField(fieldName);
if (field != null && field.FieldType.IsArray)
{
var array = field.GetValue(root) as object[];
object item = EditorUtils.Clone(field.FieldType.GetElementType(), null, false);
if (item != null)
{
Array.Resize(ref array, array.Length + 1);
array[^1] = item;
field.SetValue(root, array);
}
}
else if (field != null)
{
object item = EditorUtils.Clone(field.FieldType, null, false);
if (item != null)
{
field.SetValue(root, item);
}
}
}
public static void CreateNewActionButton(object root, InspectorProperty property)
{
if (SirenixEditorGUI.ToolbarButton(SdfIconType.PlusCircleDotted))
{
CreateNewAction(root, property);
}
}
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree();
tree.Config.DrawSearchToolbar = true;
tree.AddAllAssetsAtPath("Cards", "Assets/TcgEngine/Resources/Cards", typeof(CardData), true, true).SortMenuItemsByName();
tree.AddAllAssetsAtPath("Abilities", "Assets/TcgEngine/Resources/Abilities", typeof(AbilityData), true, true).SortMenuItemsByName();
tree.AddAllAssetsAtPath("Effects", "Assets/TcgEngine/Resources/Effects", typeof(EffectData), true, true).SortMenuItemsByName();
tree.AddAllAssetsAtPath("Conditions", "Assets/TcgEngine/Resources/Conditions", typeof(ConditionData), true, true).SortMenuItemsByName();
tree.AddAllAssetsAtPath("Filters", "Assets/TcgEngine/Resources/Conditions", typeof(FilterData), true, true).SortMenuItemsByName();
tree.AddAllAssetsAtPath("Status", "Assets/TcgEngine/Resources/Status", typeof(StatusData), true, true).SortMenuItemsByName();
return tree;
}
protected override void OnBeginDrawEditors()
{
if (MenuTree == null) return;
SirenixEditorGUI.BeginHorizontalToolbar(MenuTree.Config.SearchToolbarHeight);
{
GUILayout.Label("Card Editor");
if (SirenixEditorGUI.ToolbarButton(new GUIContent("Clone")))
{
var selected = MenuTree.Selection.SelectedValue as UnityEngine.Object;
if (selected == null) return;
Type selectedType = selected.GetType();
var objToSelect = EditorUtils.Clone(selectedType, selected);
if (objToSelect != null)
{
TrySelectMenuItemWithObject(objToSelect);
}
}
}
SirenixEditorGUI.EndHorizontalToolbar();
}
}
public class CardDataProcessor : OdinAttributeProcessor<CardData>
{
public override void ProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member, List<Attribute> attributes)
{
base.ProcessChildMemberAttributes(parentProperty, member, attributes);
if (member.Name == "abilities" || member.Name == "traits")
{
var attribute = attributes.GetAttribute<ListDrawerSettingsAttribute>();
attribute.OnTitleBarGUI = "@CardEditor.CreateNewActionButton($root, $property)";
}
}
}
public class AbilityDataProcessor : OdinAttributeProcessor<AbilityData>
{
public override void ProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member, List<Attribute> attributes)
{
base.ProcessChildMemberAttributes(parentProperty, member, attributes);
if (member.Name == "status" || member.Name == "chain_abilities")
{
var attribute = attributes.GetAttribute<ListDrawerSettingsAttribute>();
attribute.OnTitleBarGUI = "@CardEditor.CreateNewActionButton($root, $property)";
}
}
}
public class EditorUtils
{
private static void UpdateForType<T>(Type type, T source, T destination)
{
FieldInfo[] myObjectFields = type.GetFields(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in myObjectFields)
{
if (fi.FieldType.IsArray)
{
// Arrays de SO ou Enum são copiados por referencia
if (fi.FieldType.GetElementType().IsSubclassOf(typeof(ScriptableObject)) || fi.FieldType.GetElementType().IsEnum)
{
fi.SetValue(destination, (fi.GetValue(source) as Array).Clone());
}
// Arrays de Classes precisam que as instâncias sejam duplicadas também
else
{
var sourceArray = fi.GetValue(source) as Array;
if (sourceArray == null)
continue;
var clonedArray = Array.CreateInstance(fi.FieldType.GetElementType(), sourceArray.Length);
for (var i = 0; i < sourceArray.Length; i++)
{
if (typeof(ICloneable).IsAssignableFrom(sourceArray.GetValue(i)))
{
clonedArray.SetValue(((ICloneable)sourceArray.GetValue(i)).Clone(), i);
}
}
fi.SetValue(destination, clonedArray);
}
} else
{
fi.SetValue(destination, fi.GetValue(source));
}
}
}
public static string GetFolder(Type selectedType)
{
string SOData = selectedType.ToString();
if (selectedType.IsSubclassOf(typeof(ConditionData)))
{
SOData = "TcgEngine.ConditionData";
}
else if (selectedType.IsSubclassOf(typeof(FilterData)))
{
SOData = "TcgEngine.FilterData";
}
else if (selectedType.IsSubclassOf(typeof(EffectData)))
{
SOData = "TcgEngine.EffectData";
}
var folders = new Dictionary<string, string>
{
{ "TcgEngine.CardData", "Cards" },
{ "TcgEngine.AbilityData", "Abilities" },
{ "TcgEngine.EffectData", "Effects" },
{ "TcgEngine.ConditionData", "Conditions" },
{ "TcgEngine.FilterData", "Filters" },
{ "TcgEngine.StatusData", "Status" },
{ "TcgEngine.TraitData", "Traits" },
{ "TcgEngine.TeamData", "Teams" },
{ "TcgEngine.RarityData", "Rarities" },
};
return folders.ContainsKey(SOData) ? $"Assets/TcgEngine/Resources/{folders[SOData]}" : "Assets/TcgEngine/Resources/";
}
public static T Clone<T>(Type selectedType, object selected, bool copyValues = true) where T : ScriptableObject
{
T clone = null;
string folder = "";
if (selected == null)
{
folder = GetFolder(selectedType);
}
else
{
string[] parts = AssetDatabase.GetAssetPath(selected as UnityEngine.Object).Split("/");
Array.Resize(ref parts, parts.Length - 1);
folder = string.Join("/", parts);
}
MethodInfo showDialogMethod = typeof(ScriptableObjectCreator).GetMethod("ShowDialog");
MethodInfo genericShowDialogMethod = showDialogMethod.MakeGenericMethod(selectedType);
object[] parameters = { $"{folder}", new Action<T>(obj =>
{
if (copyValues)
UpdateForType(selectedType, selected as T, obj);
var propertyInfo = obj.GetType().GetField("id");
if (propertyInfo != null)
{
propertyInfo.SetValue(obj, obj.name.ToLower().Replace(" ", "_"));
}
clone = obj;
})};
genericShowDialogMethod.Invoke(null, parameters);
return clone;
}
public static object Clone(Type selectedType, object selected, bool copyValues = true)
{
return Clone<ScriptableObject>(selectedType, selected, copyValues);
}
}
Now there is a button (+) to create SO and connect it in just 1 click, making easier to create new Abilities/Effects/Teams/Rarities/Conditions/Filters and Status.
This will improve how cards are showned in DeckData and effects like Sumon
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using System.Collections;
using System.Collections.Generic;
using TcgEngine;
using UnityEditor;
using UnityEngine;
public class CardDrawer<T> : OdinValueDrawer<T> where T: CardData
{
protected override void DrawPropertyLayout(GUIContent label)
{
var rect = EditorGUILayout.GetControlRect(label != null, 100);
if (label != null)
{
rect.xMin = EditorGUI.PrefixLabel(rect.AlignCenterY(15), label).xMin;
}
else
{
rect = EditorGUI.IndentedRect(rect);
}
CardData card = this.ValueEntry.SmartValue;
Texture texture = null;
if (card)
{
texture = GUIHelper.GetAssetThumbnail(card.art_full, typeof(CardData), true);
GUI.Label(rect.AddXMin(120).AlignMiddle(16), EditorGUI.showMixedValue ? "-" : card.id);
}
this.ValueEntry.WeakSmartValue = SirenixEditorFields.UnityPreviewObjectField(rect.AlignLeft(100), card, texture, this.ValueEntry.BaseValueType);
}
}
Helps you to find all Cards that have a specific Ability, Name and/or AbilityTrigger.
Helps you to find all Scriptable Object Instances from a specific SO Class. Ex.: Find all instances of ConditionCardPile
Helps you to find all duplicates. Ex.: Cards with same ids
Notes:
- You MUST have TCG Engine to add the changes showned above;
- You MUST have Odin to use the attributes showned above (with demo instealled);
- Some changes are not listed here because are specific for my game;
- Some classes could not exist in your project that extend from Filter/Condition and Effect, just remove it.
- This gist was created using TCG Engine 1.09 and Odin 3.2.1.0, so is possible that in the future you will need to change minor details to make it work.