112 lines
5.4 KiB
C#
112 lines
5.4 KiB
C#
// Assets/_Project/Scripts/Editor/Gameplay/WaveGoldEntryDrawer.cs
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using TD.Gameplay;
|
||
|
||
namespace TD.Editor.Gameplay
|
||
{
|
||
/// <summary>
|
||
/// Custom property drawer for <see cref="WaveGoldEntry"/>. Renders the standard fields
|
||
/// followed by a read-only "Preview Total" line computed from the entry's values, so
|
||
/// designers can see at a glance how much a single player could earn from a wave.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// The preview includes the kill-reward portion only when the entry's <c>Wave</c>
|
||
/// reference is assigned (it's needed to count enemies in that wave). When unset, the
|
||
/// preview shows just <c>CompletionBonus + NoLeaksBonus</c> and labels itself so the
|
||
/// designer knows to drop a WaveDefinition in for a complete number.
|
||
/// </remarks>
|
||
[CustomPropertyDrawer(typeof(WaveGoldEntry))]
|
||
public class WaveGoldEntryDrawer : PropertyDrawer
|
||
{
|
||
private const float PreviewLineExtraHeight = 4f;
|
||
|
||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||
{
|
||
// Sum the children + one extra line for the preview label. We render children
|
||
// ourselves rather than using EditorGUI.PropertyField default since we want a
|
||
// foldout with our custom preview tacked onto the end.
|
||
if (!property.isExpanded) return EditorGUIUtility.singleLineHeight;
|
||
|
||
float h = EditorGUIUtility.singleLineHeight; // foldout
|
||
h += SpacedLineHeight() * 4; // Wave + GoldPerEnemy + Completion + NoLeaks
|
||
h += SpacedLineHeight() + PreviewLineExtraHeight; // preview label
|
||
return h;
|
||
}
|
||
|
||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||
{
|
||
// Foldout header.
|
||
Rect headerRect = new Rect(position.x, position.y, position.width,
|
||
EditorGUIUtility.singleLineHeight);
|
||
property.isExpanded = EditorGUI.Foldout(headerRect, property.isExpanded, label,
|
||
toggleOnLabelClick: true);
|
||
if (!property.isExpanded) return;
|
||
|
||
float y = position.y + SpacedLineHeight();
|
||
float indent = 14f;
|
||
Rect Row()
|
||
{
|
||
Rect r = new Rect(position.x + indent, y, position.width - indent,
|
||
EditorGUIUtility.singleLineHeight);
|
||
y += SpacedLineHeight();
|
||
return r;
|
||
}
|
||
|
||
// Standard property fields.
|
||
var waveProp = property.FindPropertyRelative("Wave");
|
||
var perEnemyProp = property.FindPropertyRelative("GoldPerEnemy");
|
||
var completionProp = property.FindPropertyRelative("CompletionBonus");
|
||
var noLeaksProp = property.FindPropertyRelative("NoLeaksBonus");
|
||
|
||
EditorGUI.PropertyField(Row(), waveProp);
|
||
EditorGUI.PropertyField(Row(), perEnemyProp);
|
||
EditorGUI.PropertyField(Row(), completionProp);
|
||
EditorGUI.PropertyField(Row(), noLeaksProp);
|
||
|
||
// Compute the preview total. We instantiate the same logic as the runtime
|
||
// property — read the serialized values, count enemies in the optional Wave,
|
||
// sum it all up. Done inline (rather than calling WaveGoldEntry.PreviewTotalGold
|
||
// directly) because SerializedProperty edits aren't applied to the target object
|
||
// until ApplyModifiedProperties runs at the end of the frame, so reading the
|
||
// backing class instance here would show stale data while the user is typing.
|
||
int perEnemy = perEnemyProp.intValue;
|
||
int completion = completionProp.intValue;
|
||
int noLeaks = noLeaksProp.intValue;
|
||
var waveAsset = waveProp.objectReferenceValue as WaveDefinition;
|
||
|
||
int enemyCount = 0;
|
||
if (waveAsset != null && waveAsset.Entries != null)
|
||
{
|
||
foreach (var e in waveAsset.Entries)
|
||
{
|
||
if (e.EnemyType != null && e.Count > 0)
|
||
enemyCount += e.Count;
|
||
}
|
||
}
|
||
|
||
int total = perEnemy * enemyCount + completion + noLeaks;
|
||
|
||
// Preview line. Slightly dimmed style, italic, non-editable. Includes a
|
||
// breakdown so the designer can see where the number came from.
|
||
y += PreviewLineExtraHeight;
|
||
Rect previewRect = new Rect(position.x + indent, y, position.width - indent,
|
||
EditorGUIUtility.singleLineHeight);
|
||
|
||
string breakdown = waveAsset != null
|
||
? $"Preview Total: {total} g ({perEnemy} × {enemyCount} enemies " +
|
||
$"+ {completion} completion + {noLeaks} no-leak)"
|
||
: $"Preview Total: {completion + noLeaks} g (bonuses only — assign Wave " +
|
||
$"to include kill rewards)";
|
||
|
||
var prevStyle = new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic };
|
||
using (new EditorGUI.DisabledScope(true))
|
||
{
|
||
EditorGUI.LabelField(previewRect, breakdown, prevStyle);
|
||
}
|
||
}
|
||
|
||
private static float SpacedLineHeight()
|
||
=> EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||
}
|
||
}
|