using System.Collections.Generic; using UnityEngine; using VRC.SDK3.Avatars.ScriptableObjects; using VRC.SDKBase; #if UNITY_EDITOR using UnityEditor; using System.Linq; using VRC.SDK3.Avatars.Components; #endif namespace gay.lilyy.aacshared.runtimecomponents { [System.Serializable] public class PresetParameter { public string name; public VRCExpressionParameters.ValueType valueType; public float setTo; public bool shouldChange = false; } [AddComponentMenu("LillithRosePup/Parameter Preset")] public class ParameterPresetDefinition : MonoBehaviour, IEditorOnly { public string Name = "New Preset"; public string Group = "Default"; [HideInInspector] // custom inspector public List Parameters; // unity needs this to show the enable/disable box void Start() { } } #if UNITY_EDITOR [CustomEditor(typeof(ParameterPresetDefinition))] public class ParameterPresetDefinitionInspector : Editor { private ParameterPresetDefinition preset; private bool parametersLoaded = false; private void OnEnable() { preset = (ParameterPresetDefinition)target; LoadMissingParameters(); } private void LoadMissingParameters() { VRCAvatarDescriptor aviDesc = preset.gameObject.GetComponent(); if (aviDesc == null || aviDesc.expressionParameters == null || parametersLoaded) return; // Initialize Parameters list if it's null if (preset.Parameters == null) preset.Parameters = new List(); // Get parameters from the Group's ParametersFile var sourceParameters = aviDesc.expressionParameters.parameters; if (sourceParameters == null) return; // Create a dictionary of source parameters for quick lookup var sourceParamDict = sourceParameters.ToDictionary(p => p.name, p => p); // Create a set of source parameter names var sourceParameterNames = new HashSet(sourceParamDict.Keys); // Remove parameters that exist in preset but not in source bool hasChanges = false; preset.Parameters.RemoveAll(presetParam => { if (!sourceParameterNames.Contains(presetParam.name)) { hasChanges = true; return true; // Remove this parameter } return false; // Keep this parameter }); // Add missing parameters and update existing ones foreach (var sourceParam in sourceParameters) { var existingParam = preset.Parameters.FirstOrDefault(p => p.name == sourceParam.name); if (existingParam == null) { // Add new parameter var clonedParam = new PresetParameter { name = sourceParam.name, valueType = sourceParam.valueType, setTo = sourceParam.defaultValue, shouldChange = false, }; preset.Parameters.Add(clonedParam); hasChanges = true; } else { // Update existing parameter to match source type if (existingParam.valueType != sourceParam.valueType) { existingParam.valueType = sourceParam.valueType; hasChanges = true; } } } if (hasChanges) { EditorUtility.SetDirty(preset); Debug.Log($"Updated parameters in preset '{preset.Name}' to match source parameters file"); } parametersLoaded = true; } private int GetPresetIndex() { VRCAvatarDescriptor aviDesc = preset.gameObject.GetComponent(); if (aviDesc == null) return -1; var presets = aviDesc.GetComponentsInChildren().ToList().Where(p => p.Group == preset.Group).ToList(); for (int i = 0; i < presets.Count; i++) { if (presets[i] == preset) { return i + 1; // 1-based index matching ParameterPreset.cs } } return -1; } public override void OnInspectorGUI() { // Ensure parameters are loaded when inspector is drawn LoadMissingParameters(); // Draw the default inspector DrawDefaultInspector(); // Display preset index int presetIndex = GetPresetIndex(); if (presetIndex > 0) { EditorGUILayout.Space(); EditorGUILayout.HelpBox($"Preset Index: {presetIndex}\n(This preset will be activated when the 'Preset {preset.Group}' parameter equals {presetIndex})", MessageType.Info); } if (GUILayout.Button("Copy Parameter Name")) { GUIUtility.systemCopyBuffer = "Preset " + preset.Group; EditorGUILayout.LabelField("Parameter Name Copied to Clipboard", EditorStyles.boldLabel); } // Custom parameter list rendering if (preset.Parameters != null && preset.Parameters.Count > 0) { EditorGUILayout.Space(); EditorGUILayout.LabelField("Parameters", EditorStyles.boldLabel); for (int i = 0; i < preset.Parameters.Count; i++) { var param = preset.Parameters[i]; EditorGUILayout.BeginHorizontal(); // Parameter name EditorGUILayout.LabelField(param.name, GUILayout.Width(150)); // Parameter type (read-only) EditorGUILayout.LabelField($"Type: {param.valueType}", GUILayout.Width(100)); // Should change toggle param.shouldChange = EditorGUILayout.Toggle(param.shouldChange, GUILayout.Width(60)); // Set to value (only show if shouldChange is true) if (param.shouldChange) { switch (param.valueType) { case VRCExpressionParameters.ValueType.Bool: param.setTo = EditorGUILayout.Toggle(param.setTo > 0.5f) ? 1f : 0f; break; case VRCExpressionParameters.ValueType.Int: param.setTo = EditorGUILayout.IntField((int)param.setTo); break; case VRCExpressionParameters.ValueType.Float: param.setTo = EditorGUILayout.FloatField(param.setTo); break; } } else { EditorGUILayout.LabelField("(unchanged)", GUILayout.Width(80)); } EditorGUILayout.EndHorizontal(); } } // Add a button to manually reload parameters EditorGUILayout.Space(); if (GUILayout.Button("Reload Parameters from Group")) { parametersLoaded = false; LoadMissingParameters(); } // Add a button to clear all parameters if (GUILayout.Button("Clear All Parameters")) { if (EditorUtility.DisplayDialog("Clear Parameters", "Are you sure you want to clear all parameters from this preset?", "Yes", "No")) { preset.Parameters.Clear(); EditorUtility.SetDirty(preset); } } } } #endif }