Files
barotrauma-localmods/Quick Interactions/CSharp/Client/CrabUI/CUI.cs

301 lines
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
using System.IO;
using Barotrauma;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using HarmonyLib;
using System.Runtime.CompilerServices;
[assembly: IgnoresAccessChecksTo("Barotrauma")]
[assembly: IgnoresAccessChecksTo("DedicatedServer")]
[assembly: IgnoresAccessChecksTo("BarotraumaCore")]
namespace QICrabUI
{
/// <summary>
/// In fact a static class managing static things
/// </summary>
public partial class CUI
{
public static Vector2 GameScreenSize => new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
public static Rectangle GameScreenRect => new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
private static string modDir;
public static string ModDir
{
get => modDir;
set
{
modDir = value;
LuaFolder = Path.Combine(value, @"Lua");
}
}
public static bool UseLua { get; set; } = true;
public static string LuaFolder { get; set; }
private static string assetsPath;
public static string AssetsPath
{
get => assetsPath;
set
{
assetsPath = value;
PalettesPath = Path.Combine(value, @"Palettes");
}
}
public static string CUITexturePath = "CUI.png";
public static string PalettesPath { get; set; }
/// <summary>
/// If set CUI will also check this folder when loading textures
/// </summary>
public static string PGNAssets
{
get => TextureManager.PGNAssets;
set => TextureManager.PGNAssets = value;
}
private static List<CUI> Instances = new List<CUI>();
/// <summary>
/// The singleton
/// </summary>
public static CUI Instance
{
get
{
if (Instances.Count == 0) return null;
return Instances.First();
}
set
{
Instances.Clear();
if (value != null) Instances.Add(value);
}
}
/// <summary>
/// Orchestrates Drawing and updates, there could be only one
/// CUI.Main is located under vanilla GUI
/// </summary>
public static CUIMainComponent Main => Instance?.main;
/// <summary>
/// Orchestrates Drawing and updates, there could be only one
/// CUI.TopMain is located above vanilla GUI
/// </summary>
public static CUIMainComponent TopMain => Instance?.topMain;
/// <summary>
/// Snapshot of mouse and keyboard state
/// </summary>
public static CUIInput Input => Instance?.input;
/// <summary>
/// Safe texture manager
/// </summary
public static CUITextureManager TextureManager => Instance?.textureManager;
/// <summary>
/// Adapter to vanilla focus system, don't use
/// </summary>
public static CUIFocusResolver FocusResolver => Instance?.focusResolver;
public static CUILuaRegistrar LuaRegistrar => Instance?.luaRegistrar;
public static CUIComponent FocusedComponent
{
get => FocusResolver.FocusedCUIComponent;
set => FocusResolver.FocusedCUIComponent = value;
}
/// <summary>
/// This affects logging
/// </summary>
public static bool Debug;
/// <summary>
/// Will break the mod if it's compiled
/// </summary>
public static bool UseCursedPatches { get; set; } = false;
/// <summary>
/// It's important to set it, if 2 CUIs try to add a hook with same id one won't be added
/// </summary>
public static string HookIdentifier
{
get => hookIdentifier;
set
{
hookIdentifier = value?.Replace(' ', '_');
}
}
private static string hookIdentifier = "";
public static string CUIHookID => $"QICrabUI.{HookIdentifier}";
public static Harmony harmony;
public static Random Random = new Random();
/// <summary>
/// Called on first Initialize
/// </summary>
public static event Action OnInit;
/// <summary>
/// Called on last Dispose
/// </summary>
public static event Action OnDispose;
public static bool Disposed { get; set; } = true;
public static event Action<TextInputEventArgs> OnWindowTextInput;
public static event Action<TextInputEventArgs> OnWindowKeyDown;
//public static event Action<TextInputEventArgs> OnWindowKeyUp;
//TODO this doesn't trigger when you press menu button, i need to go inside thet method
public static event Action OnPauseMenuToggled;
public static void InvokeOnPauseMenuToggled() => OnPauseMenuToggled?.Invoke();
public static bool InputBlockingMenuOpen
{
get
{
if (IsBlockingPredicates == null) return false;
return IsBlockingPredicates.Any(p => p());
}
}
public static List<Func<bool>> IsBlockingPredicates => Instance?.isBlockingPredicates;
private List<Func<bool>> isBlockingPredicates = new List<Func<bool>>();
/// <summary>
/// In theory multiple mods could use same CUI instance,
/// i clean it up when UserCount drops to 0
/// </summary>
public static int UserCount = 0;
/// <summary>
/// An object that contains current mouse and keyboard states
/// It scans states at the start on Main.Update
/// </summary>
private CUIInput input = new CUIInput();
private CUIMainComponent main;
private CUIMainComponent topMain;
private CUITextureManager textureManager = new CUITextureManager();
private CUIFocusResolver focusResolver = new CUIFocusResolver();
private CUILuaRegistrar luaRegistrar = new CUILuaRegistrar();
public static void ReEmitWindowTextInput(object sender, TextInputEventArgs e) => OnWindowTextInput?.Invoke(e);
public static void ReEmitWindowKeyDown(object sender, TextInputEventArgs e) => OnWindowKeyDown?.Invoke(e);
//public static void ReEmitWindowKeyUp(object sender, TextInputEventArgs e) => OnWindowKeyUp?.Invoke(e);
private void CreateMains()
{
main = new CUIMainComponent() { AKA = "Main Component" };
topMain = new CUIMainComponent() { AKA = "Top Main Component" };
}
/// <summary>
/// Should be called in IAssemblyPlugin.Initialize
/// \todo make it CUI instance member when plugin system settle
/// </summary>
public static void Initialize()
{
CUIDebug.Log($"CUI.Initialize {HookIdentifier} Instance:[{Instance?.GetHashCode()}] UserCount:{UserCount}", Color.Lime);
if (Instance == null)
{
Disposed = false;
Instance = new CUI();
Stopwatch sw = Stopwatch.StartNew();
if (HookIdentifier == null || HookIdentifier == "") CUI.Warning($"Warning: CUI.HookIdentifier is not set, this mod may conflict with other mods that use CUI");
InitStatic();
// this should init only static stuff that doesn't depend on instance
OnInit?.Invoke();
Instance.CreateMains();
GameMain.Instance.Window.TextInput += ReEmitWindowTextInput;
GameMain.Instance.Window.KeyDown += ReEmitWindowKeyDown;
//GameMain.Instance.Window.KeyUp += ReEmitWindowKeyUp;
CUIDebug.Log($"CUI.OnInit?.Invoke took {sw.ElapsedMilliseconds}ms");
sw.Restart();
harmony = new Harmony(CUIHookID);
PatchAll();
CUIDebug.Log($"CUI.PatchAll took {sw.ElapsedMilliseconds}ms");
AddCommands();
sw.Restart();
LuaRegistrar.Register();
CUIDebug.Log($"CUI.LuaRegistrar.Register took {sw.ElapsedMilliseconds}ms");
}
UserCount++;
CUIDebug.Log($"CUI.Initialized {HookIdentifier} Instance:[{Instance?.GetHashCode()}] UserCount:{UserCount}", Color.Lime);
}
public static void OnLoadCompleted()
{
//Idk doesn't work
//CUIMultiModResolver.FindOtherInputs();
}
/// <summary>
/// Should be called in IAssemblyPlugin.Dispose
/// </summary>
public static void Dispose()
{
CUIDebug.Log($"CUI.Dispose {HookIdentifier} Instance:[{Instance?.GetHashCode()}] UserCount:{UserCount}", Color.Lime);
UserCount--;
if (UserCount <= 0)
{
RemoveCommands();
// harmony.UnpatchAll(harmony.Id);
harmony.UnpatchAll();
TextureManager.Dispose();
CUIDebugEventComponent.CapturedIDs.Clear();
OnDispose?.Invoke();
Disposed = true;
Instance.isBlockingPredicates.Clear();
Errors.Clear();
LuaRegistrar.Deregister();
Instance = null;
UserCount = 0;
CUIDebug.Log($"CUI.Disposed {HookIdentifier} Instance:[{Instance?.GetHashCode()}] UserCount:{UserCount}", Color.Lime);
}
GameMain.Instance.Window.TextInput -= ReEmitWindowTextInput;
GameMain.Instance.Window.KeyDown -= ReEmitWindowKeyDown;
//GameMain.Instance.Window.KeyUp -= ReEmitWindowKeyUp;
}
//HACK Why it's set to run in static constructor?
// it runs perfectly fine in CUI.Initialize
//TODO component inits doesn't depend on the order
// why am i responsible for initing them here?
internal static void InitStatic()
{
CUIExtensions.InitStatic();
CUIInterpolate.InitStatic();
CUIAnimation.InitStatic();
CUIReflection.InitStatic();
CUIMultiModResolver.InitStatic();
CUIPalette.InitStatic();
CUIMap.CUIMapLink.InitStatic();
CUIMenu.InitStatic();
CUIComponent.InitStatic();
CUITypeMetaData.InitStatic();
CUIStyleLoader.InitStatic();
}
}
}