Bring in AAC!
project ive been working on for a while, just felt confident enough to move it here
This commit is contained in:
parent
e608e2a56b
commit
1d7052a258
209 changed files with 1561 additions and 74738 deletions
217
AAC/AACShared/Layers/ChildToggle.cs
Normal file
217
AAC/AACShared/Layers/ChildToggle.cs
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnimatorAsCode.V1;
|
||||
using gay.lilyy.aaccore;
|
||||
using gay.lilyy.aacshared.runtimecomponents;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.ScriptableObjects;
|
||||
using AnimatorAsCode.V1.ModularAvatar;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
|
||||
namespace gay.lilyy.aacshared.layers {
|
||||
[InitializeOnLoad]
|
||||
public class ChildToggle : LayerGroup
|
||||
{
|
||||
// remove when ready for uploaded
|
||||
public override bool experimental => false;
|
||||
public override bool shouldWarnIfNotApplicable => false;
|
||||
private static readonly ChildToggle _instance = new();
|
||||
|
||||
static ChildToggle() {}
|
||||
|
||||
public override string DisplayName => "Child Toggle";
|
||||
public override string SystemName => "childtoggle";
|
||||
|
||||
private static ChildToggleDefinition[] getDefinitions(AACAssets assets)
|
||||
{
|
||||
return assets.ctx.AvatarRootObject.GetComponentsInChildren<ChildToggleDefinition>();
|
||||
}
|
||||
|
||||
public override bool IsApplicable(AACAssets assets)
|
||||
{
|
||||
return getDefinitions(assets).Length > 0;
|
||||
}
|
||||
|
||||
private AacFlBoolParameter createLayer(AACAssets assets, ChildToggleDefinition definition, Transform transform)
|
||||
{
|
||||
var layer = assets.fx.NewLayer($"Child toggle {definition.name} {transform.name}");
|
||||
var toggle = layer.BoolParameter($"child_{definition.name}_{transform.name}");
|
||||
|
||||
var idle = layer.NewState("Idle").WithAnimation(assets.emptyClip);
|
||||
var flipped = layer.NewState("Flipped").WithAnimation(assets.aac.NewClip().Toggling(transform.gameObject, !transform.gameObject.activeSelf));
|
||||
|
||||
idle.TransitionsTo(flipped).When(toggle.IsEqualTo(true));
|
||||
flipped.TransitionsTo(idle).When(toggle.IsEqualTo(false));
|
||||
|
||||
return toggle;
|
||||
}
|
||||
|
||||
/**
|
||||
<summary>
|
||||
im gonna be so honest i got cursor to hack together my MusicPlayer's menu creator into this monstrosity. im so sorry.
|
||||
</summary>
|
||||
<param name="assets">The AACAssets instance.</param>
|
||||
<param name="definition">The ChildToggleDefinition instance.</param>
|
||||
<param name="childParameters">A dictionary of child transforms and their corresponding toggle parameters.</param>
|
||||
<param name="sharedFolderHierarchy">A shared dictionary of folder paths to GameObjects across all definitions.</param>
|
||||
*/
|
||||
private GameObject FindOrCreateFolder(GameObject parent, string folderName, Dictionary<string, GameObject> sharedFolderHierarchy, string fullPath, Transform definitionTransform, HashSet<GameObject> allRootMenus)
|
||||
{
|
||||
// Check if folder already exists in shared hierarchy
|
||||
if (sharedFolderHierarchy.ContainsKey(fullPath))
|
||||
{
|
||||
var existingFolder = sharedFolderHierarchy[fullPath];
|
||||
// Verify it still exists and is accessible
|
||||
if (existingFolder != null)
|
||||
{
|
||||
return existingFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove stale entry
|
||||
sharedFolderHierarchy.Remove(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a folder with the same name already exists as a sibling in current parent
|
||||
foreach (Transform child in parent.transform)
|
||||
{
|
||||
var existingMenuItem = child.GetComponent<ModularAvatarMenuItem>();
|
||||
if (existingMenuItem != null && existingMenuItem.label == folderName)
|
||||
{
|
||||
// Found existing folder, add it to shared hierarchy and return it
|
||||
sharedFolderHierarchy[fullPath] = child.gameObject;
|
||||
return child.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
// Check all other root menus for a folder with the same name at the same level
|
||||
// This prevents duplicate folders when multiple definitions share the same MenuPath prefix
|
||||
foreach (var rootMenu in allRootMenus)
|
||||
{
|
||||
if (rootMenu == parent) continue; // Skip current menu
|
||||
|
||||
// If we're at root level (parent is a Menu_*), check root level of other menus
|
||||
// If we're in a nested folder, check the same nesting level
|
||||
if (parent.name.StartsWith("Menu_") && rootMenu.name.StartsWith("Menu_"))
|
||||
{
|
||||
// Check root level folders
|
||||
foreach (Transform child in rootMenu.transform)
|
||||
{
|
||||
var existingMenuItem = child.GetComponent<ModularAvatarMenuItem>();
|
||||
if (existingMenuItem != null && existingMenuItem.label == folderName)
|
||||
{
|
||||
// Found existing folder in another root menu, reuse it
|
||||
sharedFolderHierarchy[fullPath] = child.gameObject;
|
||||
return child.gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create new folder
|
||||
var folderGo = new GameObject($"Folder_{folderName}");
|
||||
folderGo.transform.SetParent(parent.transform);
|
||||
var folderMenuItem = folderGo.AddComponent<ModularAvatarMenuItem>();
|
||||
folderMenuItem.label = folderName;
|
||||
folderMenuItem.Control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
|
||||
folderMenuItem.MenuSource = SubmenuSource.Children;
|
||||
|
||||
sharedFolderHierarchy[fullPath] = folderGo;
|
||||
return folderGo;
|
||||
}
|
||||
|
||||
private void CreateMenu(AACAssets assets, ChildToggleDefinition definition, Dictionary<Transform, AacFlBoolParameter> childParameters, Dictionary<string, GameObject> sharedFolderHierarchy, HashSet<GameObject> allRootMenus)
|
||||
{
|
||||
if (childParameters.Count == 0) return;
|
||||
|
||||
var menuGo = new GameObject($"Menu_{definition.name}");
|
||||
menuGo.transform.SetParent(definition.transform);
|
||||
var menuItem = menuGo.AddComponent<ModularAvatarMenuItem>();
|
||||
menuItem.label = "Child Toggle";
|
||||
menuItem.Control.type = VRCExpressionsMenu.Control.ControlType.SubMenu;
|
||||
menuItem.MenuSource = SubmenuSource.Children;
|
||||
|
||||
allRootMenus.Add(menuGo);
|
||||
|
||||
GameObject firstFolder = null;
|
||||
|
||||
foreach (var kvp in childParameters)
|
||||
{
|
||||
var childTransform = kvp.Key;
|
||||
var toggleParam = kvp.Value;
|
||||
GameObject currentParent = menuGo;
|
||||
|
||||
// Create folder hierarchy if MenuPath is specified
|
||||
if (!string.IsNullOrEmpty(definition.MenuPath))
|
||||
{
|
||||
string[] folderPath = definition.MenuPath.Split('/');
|
||||
|
||||
// Create nested folder structure
|
||||
for (int i = 0; i < folderPath.Length; i++)
|
||||
{
|
||||
string folderName = folderPath[i];
|
||||
string fullPath = string.Join("/", folderPath, 0, i + 1);
|
||||
|
||||
currentParent = FindOrCreateFolder(currentParent, folderName, sharedFolderHierarchy, fullPath, definition.transform, allRootMenus);
|
||||
|
||||
// Store the first folder for MenuInstaller
|
||||
if (firstFolder == null && i == 0)
|
||||
{
|
||||
firstFolder = currentParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the toggle menu item
|
||||
var go = new GameObject($"Toggle_{childTransform.name}");
|
||||
go.transform.SetParent(currentParent.transform);
|
||||
assets.modularAvatar.EditMenuItem(go).Toggle(toggleParam).Name(childTransform.name);
|
||||
}
|
||||
|
||||
// Place MenuInstaller on the first folder if it exists, otherwise on root menu
|
||||
if (firstFolder != null)
|
||||
{
|
||||
// Only add MenuInstaller if it doesn't already exist
|
||||
if (firstFolder.GetComponent<ModularAvatarMenuInstaller>() == null)
|
||||
{
|
||||
firstFolder.AddComponent<ModularAvatarMenuInstaller>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
menuGo.AddComponent<ModularAvatarMenuInstaller>();
|
||||
}
|
||||
}
|
||||
|
||||
private void runDefinition(AACAssets assets, ChildToggleDefinition definition, Dictionary<string, GameObject> sharedFolderHierarchy, HashSet<GameObject> allRootMenus)
|
||||
{
|
||||
var childParameters = new Dictionary<Transform, AacFlBoolParameter>();
|
||||
|
||||
foreach (Transform child in definition.transform)
|
||||
{
|
||||
var toggleParam = createLayer(assets, definition, child);
|
||||
childParameters[child] = toggleParam;
|
||||
}
|
||||
|
||||
CreateMenu(assets, definition, childParameters, sharedFolderHierarchy, allRootMenus);
|
||||
}
|
||||
|
||||
public override void Run(AACAssets assets)
|
||||
{
|
||||
var definitions = getDefinitions(assets);
|
||||
Logger.LogDebug($"Child Toggle system: Found {definitions.Length} child toggle definitions");
|
||||
|
||||
// Shared folder hierarchy across all definitions to prevent duplicates
|
||||
var sharedFolderHierarchy = new Dictionary<string, GameObject>();
|
||||
// Track all root menus to check for duplicate folders
|
||||
var allRootMenus = new HashSet<GameObject>();
|
||||
|
||||
foreach (var definition in definitions)
|
||||
{
|
||||
runDefinition(assets, definition, sharedFolderHierarchy, allRootMenus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue