using System; using System.Reflection; using HarmonyLib; using RobocraftX; using RobocraftX.GUI; using RobocraftX.GUI.MyGamesScreen; using RobocraftX.Multiplayer; using Svelto.ECS; using Svelto.ECS.Experimental; using Techblox.GameSelection; using TechbloxModdingAPI.Engines; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.App { public class GameMenuEngine : IFactoryEngine { public WrappedHandler EnterMenu; public WrappedHandler ExitMenu; public IEntityFactory Factory { set; private get; } public string Name => "TechbloxModdingAPIGameInfoGameEngine"; public bool isRemovable => false; public EntitiesDB entitiesDB { set; private get; } public GameMenuEngine() { MenuEnteredEnginePatch.EnteredExitedMenu = () => { if (IsInMenu) EnterMenu.Invoke(this, new MenuEventArgs { }); else ExitMenu.Invoke(this, new MenuEventArgs { }); }; } public void Dispose() { } public void Ready() { MenuEnteredEnginePatch.IsInMenu = true; // At first it uses ActivateMenu(), then GoToMenu() which is patched MenuEnteredEnginePatch.EnteredExitedMenu(); } // game functionality public bool IsInMenu => MenuEnteredEnginePatch.IsInMenu; public Game[] GetMyGames() { EntityCollection mgsevs = entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.MyGames); var mgsevsB = mgsevs.ToBuffer().buffer; Game[] games = new Game[mgsevs.count]; for (int i = 0; i < mgsevs.count; i++) { Utility.Logging.MetaDebugLog($"Found game named {mgsevsB[i].GameName}"); games[i] = new Game(mgsevsB[i].ID); } return games; } public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L) { EntityInitializer eci = Factory.BuildEntity(id); eci.Init(new MyGameDataEntityStruct { SavedGamePath = new ECSString(path), ThumbnailId = thumbnailId, GameName = new ECSString(gameName), CreatorName = new ECSString(creatorName), GameDescription = new ECSString(description), CreatedDate = createdDate, }); // entitiesDB.PublishEntityChange(id); // this will always fail return true; } public uint HighestID() { EntityCollection games = entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.MyGames); var gamesB = games.ToBuffer().buffer; uint max = 0; for (int i = 0; i < games.count; i++) { if (gamesB[i].ID.entityID > max) { max = gamesB[i].ID.entityID; } } return max; } public bool EnterGame(EGID id) { if (!ExistsGameInfo(id)) return false; ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id); return EnterGame(mgdes.GameName, mgdes.FileId); } public bool EnterGame(ECSString gameName, string fileId, bool autoEnterSim = false) { FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer; ref var selection = ref entitiesDB.QueryEntity(GameSelectionConstants.GameSelectionEGID); selection.userContentID.Set(fileId); selection.triggerStart = true; selection.saveType = SaveType.ExistingSave; selection.saveName = gameName; selection.gameMode = GameMode.PlayGame; selection.gameID.Set("GAMEID_Road_Track"); //TODO: Expose to the API return true; } public bool SetGameName(EGID id, string name) { if (!ExistsGameInfo(id)) return false; GetGameInfo(id).GameName.Set(name); GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name); return true; } public bool SetGameDescription(EGID id, string name) { if (!ExistsGameInfo(id)) return false; GetGameInfo(id).GameDescription.Set(name); GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name); return true; } public bool ExistsGameInfo(EGID id) { return entitiesDB.Exists(id); } public ref MyGameDataEntityStruct GetGameInfo(EGID id) { return ref GetComponent(id); } public dynamic GetGameViewInfo(EGID id) { dynamic structOptional = AccessTools.Method("TechbloxModdingAPI.Utility.NativeApiExtensions:QueryEntityOptional", new []{typeof(EntitiesDB), typeof(EGID)}) .MakeGenericMethod(AccessTools.TypeByName("RobocraftX.GUI.MyGamesScreen.MyGamesSlotEntityViewStruct")) .Invoke(null, new object[] {entitiesDB, new EGID(id.entityID, MyGamesScreenExclusiveGroups.GameSlotGuiEntities)}); if (structOptional == null) throw new Exception("Could not get game slot entity"); return structOptional ? structOptional : null; } public ref T GetComponent(EGID id) where T: unmanaged, IEntityComponent { return ref entitiesDB.QueryEntity(id); } internal void CloseBetaPopup() { var (buffer, count) = entitiesDB.QueryEntities(ExclusiveGroup.Search("BetaPopup")); for (int index = 0; index < count; ++index) { entitiesDB.QueryEntity(buffer[index].TogglePanelButtonComponent.targetPanel) .guiRoot.enabled = false; } } } internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor { } [HarmonyPatch] static class MenuEnteredEnginePatch { internal static bool IsInMenu; internal static Action EnteredExitedMenu; public static void Postfix() { IsInMenu = true; EnteredExitedMenu(); } public static MethodBase TargetMethod() { return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu"); } } [HarmonyPatch] static class MenuExitedEnginePatch { public static void Prefix() { MenuEnteredEnginePatch.IsInMenu = false; MenuEnteredEnginePatch.EnteredExitedMenu(); } public static MethodBase TargetMethod() { return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame"); } } public struct MenuEventArgs { } }