Code/Studio/StudioSession.cs

UI/session component for the character editor. Manages library, editing state, current draft/target avatar, view switching, save/import/delete/favorite operations and scene opening.

File Access
using System;

namespace CharacterEditor;
public sealed class StudioSession : Component
{
	[Property]
	public bool StartInPlaza { get; set; }

	[Property]
	public bool Embedded { get; set; }

	[Property]
	public bool EnableFaceLab { get; set; }

	[Property]
	public bool EnableEscapeMenu { get; set; }

	public static void OpenScene(string path)
	{
		var options = new SceneLoadOptions
		{
			ShowLoadingScreen = false
		};
		options.SetScene(path);
		Game.ChangeScene(options);
	}

	public CharacterLibrary Library { get; } = new();
	public CharacterPreset Draft { get; private set; }
	public CharacterAvatar Target { get; private set; }
	public string View { get; set; } = "library";
	public string Message { get; set; }
	public int Revision { get; private set; }
	public bool IsNew { get; private set; }
	public string SelectedId { get; set; }
	public CharacterPreset Selected => Library.Items.FirstOrDefault(x => x.Id == SelectedId);
	public CharacterPlaza Plaza => Scene.GetAllComponents<CharacterPlaza>().FirstOrDefault();

	bool loaded;
	string previousView = "library";
	public void EnsureLoaded()
	{
		if (loaded)
			return;
		loaded = true;
		Library.Reload();
		if (Embedded)
			View = "hidden";
		else if (StartInPlaza)
			View = "plaza";
	}

	protected override void OnStart()
	{
		EnsureLoaded();
	}

	public void Edit(CharacterPreset preset = null, CharacterAvatar target = null)
	{
		EnsureLoaded();
		previousView = View == "editor" ? "library" : View;
		Target = target;
		IsNew = preset is null;
		Draft = preset?.DeepCopy() ?? new CharacterPreset
		{
			Appearance = AppearanceCatalog.CreateDefault()
		};
		if (preset is null)
			UnifiedHuman.Enable(Draft.Appearance);
		View = "editor";
		Revision++;
		Message = null;
	}

	public string Save(string name, CharacterAppearanceData appearance, bool asCopy = false)
	{
		try
		{
			var value = Draft.DeepCopy();
			value.Name = name;
			value.Appearance = appearance.DeepCopy();
			if (asCopy)
				value.Id = Guid.NewGuid().ToString("N");
			Library.Save(value);
			if (Target.IsValid())
				Target.Apply(value);
			SelectedId = value.Id;
			Draft = null;
			Target = null;
			View = previousView;
			Revision++;
			Message = $"Saved {value.Name}.";
			Plaza?.Refresh();
			return null;
		}
		catch (Exception e)
		{
			return e.Message;
		}
	}

	public void CloseEditor()
	{
		Draft = null;
		Target = null;
		View = previousView;
		Revision++;
	}

	public void Favorite(CharacterPreset preset)
	{
		try
		{
			var copy = preset.DeepCopy();
			copy.Favorite = !copy.Favorite;
			Library.Save(copy);
		}
		catch (Exception e)
		{
			Message = e.Message;
		}
	}

	public void Delete(CharacterPreset preset)
	{
		try
		{
			Library.Delete(preset.Id);
			SelectedId = null;
			Plaza?.Refresh();
			Message = $"Deleted {preset.Name}.";
		}
		catch (Exception e)
		{
			Message = e.Message;
		}
	}

	public void Import(string json)
	{
		try
		{
			var value = Library.Import(json);
			SelectedId = value.Id;
			Message = $"Imported {value.Name} as a new character.";
			Plaza?.Refresh();
		}
		catch (Exception e)
		{
			Message = e.Message;
		}
	}

	public void ShowPlaza()
	{
		View = "plaza";
		Plaza?.Refresh();
	}
}