Skip to content

Instantly share code, notes, and snippets.

@retasretas
Created August 17, 2020 09:07
Show Gist options
  • Save retasretas/9bc7368e5d814088baa1728e80882e4e to your computer and use it in GitHub Desktop.
Save retasretas/9bc7368e5d814088baa1728e80882e4e to your computer and use it in GitHub Desktop.
#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Demos.RPGEditor
{
public class ConsumableItem : Item
{
[SuffixLabel("seconds ", true)]
[BoxGroup(STATS_BOX_GROUP)]
public float Cooldown;
[HorizontalGroup(STATS_BOX_GROUP + "/Dur")]
public bool ConsumeOverTime;
[HideLabel]
[HorizontalGroup(STATS_BOX_GROUP + "/Dur")]
[SuffixLabel("seconds ", true), EnableIf("ConsumeOverTime")]
[LabelWidth(20)]
public float Duration;
[VerticalGroup(LEFT_VERTICAL_GROUP)]
public StatList Modifiers; // <<--------------- ここで使われる
public override ItemTypes[] SupportedItemTypes
{
get
{
return new ItemTypes[]
{
ItemTypes.Consumable,
ItemTypes.Flask
};
}
}
}
}
#endif
#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Demos.RPGEditor
{
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using System.Collections;
#endif
//
// The StatList is a dictionary-like list of StatValues, which holds a StatType and a value.
// This could be used by many things throughout the system. In this case, StatLists are used
// by the Character and items to define requirements and modifiers. But one could imagine
// that many things in a game could have StatLists.
//
// The reason for it being a list instead of a dictioanry is, that most often StatLists doesn't
// contain very many stats. For instance, a shield might add some defences, and a few other random bonuses,
// and iterating over a dozen values, is actually faster than making a dictionary lookup if optimized.
//
// The StatList is then customized with the ValueDropdown attribute, where we override how elements
// are added and provide the user with a list of types to choose from using OdinSelectors.
// Checkout the CustomAddStatsButton at the bottom of this script.
//
[Serializable]
public class StatList
{
[SerializeField]
[ValueDropdown("CustomAddStatsButton", IsUniqueList = true, DrawDropdownForListElements = false, DropdownTitle = "Modify Stats")]
[ListDrawerSettings(DraggableItems = false, Expanded = true)]
private List<StatValue> stats = new List<StatValue>();
public StatValue this[int index]
{
get { return this.stats[index]; }
set { this.stats[index] = value; }
}
public int Count
{
get { return this.stats.Count; }
}
public float this[StatType type]
{
get
{
for (int i = 0; i < this.stats.Count; i++)
{
if (this.stats[i].Type == type)
{
return this.stats[i].Value;
}
}
return 0;
}
set
{
for (int i = 0; i < this.stats.Count; i++)
{
if (this.stats[i].Type == type)
{
var val = this.stats[i];
val.Value = value;
this.stats[i] = val;
return;
}
}
this.stats.Add(new StatValue(type, value));
}
}
#if UNITY_EDITOR
// Finds all available stat-types and excludes the types that the statList already contains, so we don't get multiple entries of the same type.
private IEnumerable CustomAddStatsButton()
{
return Enum.GetValues(typeof(StatType)).Cast<StatType>()
.Except(this.stats.Select(x => x.Type))
.Select(x => new StatValue(x))
.AppendWith(this.stats)
.Select(x => new ValueDropdownItem(x.Type.ToString(), x));
}
#endif
}
#if UNITY_EDITOR
//
// Since the StatList is just a class that contains a list, all StatLists would contain an extra
// label with a foldout in the inspector, which we don't want.
//
// So with this drawer, we simply take the label of the member that holds the StatsList, and render the
// actual list using that label.
//
// So instead of the "private List<StatValue> stats" field getting a label named "Stats"
// It now gets the label of whatever member holds the actual StatsList
//
// If this confuses you, try out commenting the drawer below, and take a look at an item in the RPGEditor to see
// the difference.
//
internal class StatListValueDrawer : OdinValueDrawer<StatList>
{
protected override void DrawPropertyLayout(GUIContent label)
{
// This would be the "private List<StatValue> stats" field.
this.Property.Children[0].Draw(label);
}
}
#endif
}
#endif
#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Demos.RPGEditor
{
public enum StatType
{
Shooting,
Melee,
Social,
Animals,
Medicine,
Cooking,
Mining,
Crafting
}
}
#endif
#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Demos.RPGEditor
{
using System;
using UnityEngine;
//
// These StatValues are used by StatLists, and are setup so the StatType cannot be changed after it has been added to the list.
// This is done by giving the Type a HideInInspector attribute, and we then rename the Value label to be the name of the type,
// and set the LabelWidth to make it a bit more compact.
//
[Serializable]
public struct StatValue : IEquatable<StatValue>
{
[HideInInspector]
public StatType Type;
[Range(-100, 100)]
[LabelWidth(70)]
[LabelText("$Type")]
public float Value;
public StatValue(StatType type, float value)
{
this.Type = type;
this.Value = value;
}
public StatValue(StatType type)
{
this.Type = type;
this.Value = 0;
}
public bool Equals(StatValue other)
{
return this.Type == other.Type && this.Value == other.Value;
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment