validation and dialogue system

This commit is contained in:
Sebastian Bularca
2026-04-19 12:27:13 +02:00
parent 89e36b4df9
commit 8ce041e2d8
3 changed files with 188 additions and 3 deletions

View File

@@ -0,0 +1,122 @@
using Jovian.EncounterSystem;
using UnityEditor;
using UnityEngine;
namespace Jovian.EncounterSystem.Editor {
/// <summary>
/// Drawer for <see cref="DialogLineRef"/>. Shows the library + id picker on one row, the inline
/// fallback text area underneath, and a resolved preview below that. Preserves WYSIWYG — the
/// final text is always visible.
/// </summary>
[CustomPropertyDrawer(typeof(DialogLineRef))]
public class DialogLineRefDrawer : PropertyDrawer {
private const string NonePlaceholder = "<none>";
private const string EmptyLibraryPlaceholder = "<assign a library first>";
private const float PreviewHeight = 32f;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
var libraryProp = property.FindPropertyRelative("library");
var idProp = property.FindPropertyRelative("id");
var inlineProp = property.FindPropertyRelative("inlineText");
EditorGUI.BeginProperty(position, label, property);
var lineHeight = EditorGUIUtility.singleLineHeight;
var spacing = EditorGUIUtility.standardVerticalSpacing;
var libraryRect = new Rect(position.x, position.y, position.width, lineHeight);
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(libraryRect, libraryProp, label);
var libraryChanged = EditorGUI.EndChangeCheck();
using(new EditorGUI.IndentLevelScope()) {
var idRect = new Rect(position.x, position.y + lineHeight + spacing, position.width, lineHeight);
DrawIdPicker(idRect, libraryProp, idProp, libraryChanged);
var inlineRect = new Rect(
position.x,
position.y + (lineHeight + spacing) * 2,
position.width,
lineHeight * 2);
EditorGUI.PropertyField(inlineRect, inlineProp, new GUIContent("Inline"));
var previewRect = new Rect(
position.x,
position.y + (lineHeight + spacing) * 2 + lineHeight * 2 + spacing,
position.width,
PreviewHeight);
DrawPreview(previewRect, libraryProp, idProp, inlineProp);
}
EditorGUI.EndProperty();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
var lineHeight = EditorGUIUtility.singleLineHeight;
var spacing = EditorGUIUtility.standardVerticalSpacing;
// library + id + (inline 2 lines) + preview
return (lineHeight + spacing) * 2 + lineHeight * 2 + spacing + PreviewHeight;
}
private static void DrawIdPicker(Rect rect, SerializedProperty libraryProp, SerializedProperty idProp, bool libraryChanged) {
var library = libraryProp.objectReferenceValue as DialogLineLibrary;
if(library == null || library.lines == null || library.lines.Count == 0) {
using(new EditorGUI.DisabledScope(true)) {
EditorGUI.Popup(rect, "Id", 0, new[] { EmptyLibraryPlaceholder });
}
if(libraryChanged) {
idProp.stringValue = string.Empty;
}
return;
}
var count = library.lines.Count;
var ids = new string[count + 1];
var names = new string[count + 1];
ids[0] = string.Empty;
names[0] = NonePlaceholder;
var currentIndex = 0;
for(int i = 0; i < count; i++) {
var line = library.lines[i];
var id = line?.id ?? string.Empty;
ids[i + 1] = id;
names[i + 1] = string.IsNullOrEmpty(id) ? $"<unnamed {i}>" : id;
if(!libraryChanged && id == idProp.stringValue && !string.IsNullOrEmpty(id)) {
currentIndex = i + 1;
}
}
if(libraryChanged) {
idProp.stringValue = string.Empty;
currentIndex = 0;
}
var newIndex = EditorGUI.Popup(rect, "Id", currentIndex, names);
if(newIndex != currentIndex) {
idProp.stringValue = ids[newIndex];
}
}
private static void DrawPreview(Rect rect, SerializedProperty libraryProp, SerializedProperty idProp, SerializedProperty inlineProp) {
var library = libraryProp.objectReferenceValue as DialogLineLibrary;
var id = idProp.stringValue;
var inline = inlineProp.stringValue;
string resolved = null;
if(library != null && !string.IsNullOrEmpty(id)) {
resolved = library.Resolve(id);
}
if(string.IsNullOrEmpty(resolved)) {
resolved = inline;
}
var style = new GUIStyle(EditorStyles.helpBox) {
fontStyle = FontStyle.Italic,
wordWrap = true
};
var label = string.IsNullOrEmpty(resolved) ? "<no text will display>" : resolved;
EditorGUI.LabelField(rect, new GUIContent("Preview: " + label), style);
}
}
}

View File

@@ -160,7 +160,13 @@ namespace Jovian.EncounterSystem.Editor {
for(int o = 0; o < optionSet.options.Count; o++) {
var option = optionSet.options[o];
if(option?.events == null) {
if(option == null) {
continue;
}
ValidateDialogLineRef(optionSet, encounter, $"options[{o}].text", option.text, issues);
if(option.events == null) {
continue;
}
@@ -190,6 +196,43 @@ namespace Jovian.EncounterSystem.Editor {
}
}
private static void ValidateDialogLineRef(EncounterDialogOptionSet optionSet, Encounter encounter, string path, DialogLineRef lineRef, List<ValidationIssue> issues) {
var hasLibrary = lineRef.library != null;
var hasId = !string.IsNullOrEmpty(lineRef.id);
var hasInline = !string.IsNullOrEmpty(lineRef.inlineText);
if(!hasLibrary && !hasInline) {
issues.Add(new ValidationIssue {
asset = optionSet,
encounter = encounter,
path = path,
severity = ValidationSeverity.Warning,
message = "Dialog line is empty (no library reference and no inline text)."
});
return;
}
if(hasLibrary && hasId && string.IsNullOrEmpty(lineRef.library.Resolve(lineRef.id))) {
issues.Add(new ValidationIssue {
asset = optionSet,
encounter = encounter,
path = path,
severity = ValidationSeverity.Error,
message = $"DialogLineRef id '{lineRef.id}' not found in library '{lineRef.library.name}'."
});
}
if(hasLibrary && !hasId && !hasInline) {
issues.Add(new ValidationIssue {
asset = optionSet,
encounter = encounter,
path = path,
severity = ValidationSeverity.Warning,
message = "Library assigned but no id picked; will fall back to empty inline text."
});
}
}
private static void ValidateReward(Reward reward, List<ValidationIssue> issues) {
if(reward == null) {
return;