using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using KKAPI.Chara;
using KKAPI.Studio.SaveLoad;
using KKAPI.Studio.UI;
using Studio;
using UniRx;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace KKAPI.Studio
{
///
/// Provides a way to add custom menu items to CharaStudio, and gives useful methods for interfacing with the studio.
///
public static partial class StudioAPI
{
private static readonly List _customCurrentStateCategories = new List();
private static GameObject _customStateRoot;
///
/// Add a new custom category to the Anim > CurrentState tab in the studio top-left menu.
/// Can use this at any point.
///
[Obsolete("Use GetOrCreateCurrentStateCategory instead")]
public static void CreateCurrentStateCategory(CurrentStateCategory category)
{
if (category == null) throw new ArgumentNullException(nameof(category));
if (!InsideStudio)
{
KoikatuAPI.Logger.LogDebug("Tried to run StudioAPI.CreateCurrentStateCategory outside of studio!");
return;
}
if (StudioLoaded)
CreateCategory(category);
_customCurrentStateCategories.Add(category);
}
///
/// Add a new custom category to the Anim > CurrentState tab in the studio top-left menu.
/// Can use this at any point. Always returns null outside of studio.
/// If the name is empty or null, the Misc/Other category is returned.
///
public static CurrentStateCategory GetOrCreateCurrentStateCategory(string name)
{
if (!InsideStudio)
{
KoikatuAPI.Logger.LogDebug("Tried to run StudioAPI.CreateCurrentStateCategory outside of studio!");
return null;
}
if (string.IsNullOrEmpty(name)) name = "Misc/Other";
var existing = _customCurrentStateCategories.FirstOrDefault(x => x.CategoryName == name);
if (existing != null) return existing;
var newCategory = new CurrentStateCategory(name);
if (StudioLoaded)
CreateCategory(newCategory);
_customCurrentStateCategories.Add(newCategory);
return newCategory;
}
///
/// Get all instances of this controller that belong to characters that are selected in Studio's Workspace.
///
public static IEnumerable GetSelectedControllers() where T : CharaCustomFunctionController
{
return GetSelectedCharacters().Select(x => x.charInfo?.GetComponent()).Where(x => x != null);
}
///
/// Get all character objects currently selected in Studio's Workspace.
///
public static IEnumerable GetSelectedCharacters()
{
return GetSelectedObjects().OfType();
}
///
/// Get all objects (all types) currently selected in Studio's Workspace.
///
public static IEnumerable GetSelectedObjects()
{
if (!StudioLoaded) return Enumerable.Empty();
return GuideObjectManager.Instance.selectObjectKey.Select(global::Studio.Studio.GetCtrlInfo).Where(x => x != null);
}
private static void CreateCategory(CurrentStateCategory category)
{
if (category == null) throw new ArgumentNullException(nameof(category));
if (_customStateRoot == null)
_customStateRoot = GameObject.Find("StudioScene/Canvas Main Menu/02_Manipulate/00_Chara/01_State/Viewport/Content");
category.CreateCategory(_customStateRoot);
}
internal static void Init(bool insideStudio)
{
InsideStudio = insideStudio;
if (!insideStudio) return;
Hooks.SetupHooks();
StudioSaveLoadApi.Init();
SceneManager.sceneLoaded += SceneLoaded;
DebugControls();
}
///
/// True if we are currently inside CharaStudio.exe
///
public static bool InsideStudio { get; private set; }
///
/// True inside studio after it finishes loading the interface (when the starting loading screen finishes),
/// right before custom controls are created.
///
public static bool StudioLoaded { get; private set; }
///
/// Fires once after studio finished loading the interface, right before custom controls are created.
///
public static event EventHandler StudioLoadedChanged;
private static void SceneLoaded(Scene scene, LoadSceneMode loadSceneMode)
{
if (!StudioLoaded && scene.name == "Studio")
{
StudioLoaded = true;
if (StudioLoadedChanged != null)
{
foreach (var callback in StudioLoadedChanged.GetInvocationList())
{
try
{
((EventHandler) callback)(KoikatuAPI.Instance, EventArgs.Empty);
}
catch (Exception e)
{
KoikatuAPI.Logger.LogError(e);
}
}
}
foreach (var cat in _customCurrentStateCategories)
CreateCategory(cat);
}
}
[Conditional("DEBUG")]
private static void DebugControls()
{
var cat = GetOrCreateCurrentStateCategory("Control test category");
cat.AddControl(
new CurrentStateCategoryToggle(
"Test 1", 2, c =>
{
KoikatuAPI.Logger.LogMessage(c?.charInfo?.name + " - updateValue");
return 1;
}))
.Value.Subscribe(val => KoikatuAPI.Logger.LogMessage(val));
cat.AddControl(new CurrentStateCategoryToggle("Test 2", 3, c => 2)).Value.Subscribe(val => KoikatuAPI.Logger.LogMessage(val));
cat.AddControl(new CurrentStateCategoryToggle("Test 3", 4, c => 3)).Value.Subscribe(val => KoikatuAPI.Logger.LogMessage(val));
var cat2 = GetOrCreateCurrentStateCategory("Control test category");
cat2.AddControl(new CurrentStateCategorySwitch("Test add", c => true)).Value.Subscribe(val => KoikatuAPI.Logger.LogMessage(val));
cat2.AddControl(new CurrentStateCategorySlider("Test slider", c => 0.75f)).Value.Subscribe(val => KoikatuAPI.Logger.LogMessage(val));
cat2.AddControl(new CurrentStateCategoryDropdown("dropdown test", new[] {"item 1", "i2", "test 3"}, c => 1)).Value.Subscribe(val => KoikatuAPI.Logger.LogMessage("dd " + val));
}
}
}