using System;
using System.Reflection;
using HarmonyLib;
using RobocraftX.Services;
using UnityEngine;
using GamecraftModdingAPI.Utility;
using RobocraftX.Common;
namespace GamecraftModdingAPI.App
{
///
/// The Gamecraft application that is running this code right now.
///
public class Client
{
// extensible engine
protected static AppEngine appEngine = new AppEngine();
protected static Func ErrorHandlerInstanceGetter;
protected static Action EnqueueError;
protected static Action HandleErrorClosed;
///
/// An event that fires whenever the main menu is loaded.
///
public static event EventHandler EnterMenu
{
add => appEngine.EnterMenu += value;
remove => appEngine.EnterMenu -= value;
}
///
/// An event that fire whenever the main menu is exited.
///
public static event EventHandler ExitMenu
{
add => appEngine.ExitMenu += value;
remove => appEngine.ExitMenu -= value;
}
///
/// Gamecraft build version string.
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
///
/// The version.
public string Version
{
get => Application.version;
}
///
/// Unity version string.
///
/// The unity version.
public string UnityVersion
{
get => Application.unityVersion;
}
///
/// Game saves currently visible in the menu.
/// These take a second to completely populate after the EnterMenu event fires.
///
/// My games.
public Game[] MyGames
{
get
{
if (!appEngine.IsInMenu) return new Game[0];
return appEngine.GetMyGames();
}
}
///
/// Whether Gamecraft is in the Main Menu
///
/// true if in menu; false when loading or in a game.
public bool InMenu
{
get => appEngine.IsInMenu;
}
///
/// Open a popup which prompts the user to click a button.
/// This reuses Gamecraft's error dialog popup
///
/// The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt.
public void PromptUser(Error popup)
{
// if the stuff wasn't mostly set to internal, this would be written as:
// RobocraftX.Services.ErrorHandler.Instance.EqueueError(error);
object errorHandlerInstance = ErrorHandlerInstanceGetter();
EnqueueError(errorHandlerInstance, popup);
}
// TODO
/*public void CloseCurrentPrompt()
{
// RobocraftX.Services.ErrorHandler.Instance.HandlePopupClosed();
// FIXME: this is a call that is also called when closing, not the actual closing action itself (so it doesn't work)
object errorHandlerInstance = ErrorHandlerInstanceGetter();
HandleErrorClosed(errorHandlerInstance);
}*/
internal static void Init()
{
// this would have been so much simpler if this didn't involve a bunch of internal fields & classes
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
Type errorHandle = AccessTools.TypeByName("RobocraftX.Services.ErrorHandle");
ErrorHandlerInstanceGetter = (Func) AccessTools.Method("GamecraftModdingAPI.App.Client:GenInstanceGetter")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);
EnqueueError = (Action) AccessTools.Method("GamecraftModdingAPI.App.Client:GenEnqueueError")
.MakeGenericMethod(errorHandler, errorHandle)
.Invoke(null, new object[0]);
/*HandleErrorClosed = (Action) AccessTools.Method("GamecraftModdingAPI.App.Client:GenHandlePopupClosed")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);*/
// register engines
MenuEngineManager.AddMenuEngine(appEngine);
}
// Creating delegates once is faster than reflection every time
// Admittedly, this way is more difficult to code and less readable
private static Func GenInstanceGetter()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo instance = AccessTools.PropertyGetter(errorHandler, "Instance");
Func getterSimple = (Func) Delegate.CreateDelegate(typeof(Func), null, instance);
Func getterCasted = () => (object) getterSimple();
return getterCasted;
}
private static Action GenEnqueueError()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo enqueueError = AccessTools.Method(errorHandler, "EnqueueError");
Func enqueueSimple =
(Func) Delegate.CreateDelegate(typeof(Func), enqueueError);
Action enqueueCasted =
(object instance, Error error) => { enqueueSimple((T) instance, error); };
return enqueueCasted;
}
private static Action GenHandlePopupClosed()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo handlePopupClosed = AccessTools.Method(errorHandler, "HandlePopupClosed");
Action handleSimple =
(Action) Delegate.CreateDelegate(typeof(Action), handlePopupClosed);
Action handleCasted = (object instance) => handleSimple((T) instance);
return handleCasted;
}
}
}