diff --git a/TechbloxModdingAPI/App/AppEngine.cs b/TechbloxModdingAPI/App/AppEngine.cs index 3f89f26..e4f5dcb 100644 --- a/TechbloxModdingAPI/App/AppEngine.cs +++ b/TechbloxModdingAPI/App/AppEngine.cs @@ -40,23 +40,5 @@ namespace TechbloxModdingAPI.App get; private set; } = false; - - 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 struct MenuEventArgs - { - } } diff --git a/TechbloxModdingAPI/App/Client.cs b/TechbloxModdingAPI/App/Client.cs index 026b1a2..6fe4b4e 100644 --- a/TechbloxModdingAPI/App/Client.cs +++ b/TechbloxModdingAPI/App/Client.cs @@ -14,22 +14,19 @@ namespace TechbloxModdingAPI.App /// public class Client { - // extensible engine - protected static AppEngine appEngine = new AppEngine(); - - protected static Func ErrorHandlerInstanceGetter; + 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; + add => Game.menuEngine.EnterMenu += value; + remove => Game.menuEngine.EnterMenu -= value; } /// @@ -37,8 +34,8 @@ namespace TechbloxModdingAPI.App /// public static event EventHandler ExitMenu { - add => appEngine.ExitMenu += value; - remove => appEngine.ExitMenu -= value; + add => Game.menuEngine.ExitMenu += value; + remove => Game.menuEngine.ExitMenu -= value; } /// @@ -69,8 +66,8 @@ namespace TechbloxModdingAPI.App { get { - if (!appEngine.IsInMenu) return new Game[0]; - return appEngine.GetMyGames(); + if (!Game.menuEngine.IsInMenu) return Array.Empty(); + return Game.menuEngine.GetMyGames(); } } @@ -80,7 +77,7 @@ namespace TechbloxModdingAPI.App /// true if in menu; false when loading or in a game. public bool InMenu { - get => appEngine.IsInMenu; + get => Game.menuEngine.IsInMenu; } /// @@ -119,8 +116,6 @@ namespace TechbloxModdingAPI.App /*HandleErrorClosed = (Action) AccessTools.Method("TechbloxModdingAPI.App.Client:GenHandlePopupClosed") .MakeGenericMethod(errorHandler) .Invoke(null, new object[0]);*/ - // register engines - MenuEngineManager.AddMenuEngine(appEngine); } // Creating delegates once is faster than reflection every time diff --git a/TechbloxModdingAPI/App/Game.cs b/TechbloxModdingAPI/App/Game.cs index 56854b6..7f52965 100644 --- a/TechbloxModdingAPI/App/Game.cs +++ b/TechbloxModdingAPI/App/Game.cs @@ -23,7 +23,7 @@ namespace TechbloxModdingAPI.App { // extensible engines protected static GameGameEngine gameEngine = new GameGameEngine(); - protected static GameMenuEngine menuEngine = new GameMenuEngine(); + protected internal static GameMenuEngine menuEngine = new GameMenuEngine(); protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine(); protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine(); diff --git a/TechbloxModdingAPI/App/GameGameEngine.cs b/TechbloxModdingAPI/App/GameGameEngine.cs index f1ec8e7..2c6e177 100644 --- a/TechbloxModdingAPI/App/GameGameEngine.cs +++ b/TechbloxModdingAPI/App/GameGameEngine.cs @@ -8,6 +8,7 @@ using Svelto.Tasks; using Svelto.Tasks.Lean; using RobocraftX.Blocks; using RobocraftX.ScreenshotTaker; +using Techblox.Environment.Transition; using Techblox.GameSelection; using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Engines; @@ -100,7 +101,7 @@ namespace TechbloxModdingAPI.App { if (!entitiesDB.FoundInGroups()) throw new AppStateException("At least one block must exist in the world to enter simulation"); - TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB); + SwitchAnimationUtil.Start(entitiesDB); } public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid) diff --git a/TechbloxModdingAPI/App/GameMenuEngine.cs b/TechbloxModdingAPI/App/GameMenuEngine.cs index 2f65016..ac24e3a 100644 --- a/TechbloxModdingAPI/App/GameMenuEngine.cs +++ b/TechbloxModdingAPI/App/GameMenuEngine.cs @@ -1,8 +1,10 @@ using System; +using System.Reflection; using HarmonyLib; using RobocraftX; using RobocraftX.Common; +using RobocraftX.FrontEnd; using RobocraftX.GUI; using RobocraftX.GUI.MyGamesScreen; using Svelto.ECS; @@ -16,6 +18,9 @@ namespace TechbloxModdingAPI.App { public class GameMenuEngine : IFactoryEngine { + public WrappedHandler EnterMenu; + + public WrappedHandler ExitMenu; public IEntityFactory Factory { set; private get; } public string Name => "TechbloxModdingAPIGameInfoGameEngine"; @@ -24,23 +29,43 @@ namespace TechbloxModdingAPI.App 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() { - IsInMenu = false; } public void Ready() { - IsInMenu = true; + MenuEnteredEnginePatch.IsInMenu = true; // At first it uses ActivateMenu(), then GoToMenu() which is patched + MenuEnteredEnginePatch.EnteredExitedMenu(); } // game functionality - public bool IsInMenu + public bool IsInMenu => MenuEnteredEnginePatch.IsInMenu; + + public Game[] GetMyGames() { - get; - private set; - } = false; + 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) { @@ -77,10 +102,10 @@ namespace TechbloxModdingAPI.App { if (!ExistsGameInfo(id)) return false; ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id); - return EnterGame(mgdes.GameName, mgdes.SavedGamePath); + return EnterGame(mgdes.GameName, mgdes.FileId); } - public bool EnterGame(string gameName, string path, bool autoEnterSim = false) + public bool EnterGame(string gameName, string fileId, bool autoEnterSim = false) { GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build; var data = new GameSelectionData @@ -89,7 +114,8 @@ namespace TechbloxModdingAPI.App gameType = GameType.MachineEditor, saveName = gameName, saveType = SaveType.ExistingSave, - gameID = path + gameID = "GAMEID_Road_Track", //TODO: Expose to the API + userContentID = fileId }; // the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[]{data}); @@ -138,4 +164,41 @@ namespace TechbloxModdingAPI.App } 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), "GoToMenu"); + } + } + + [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 + { + + } } diff --git a/TechbloxModdingAPI/Block.cs b/TechbloxModdingAPI/Block.cs index c2f8dd8..0d730c5 100644 --- a/TechbloxModdingAPI/Block.cs +++ b/TechbloxModdingAPI/Block.cs @@ -386,8 +386,8 @@ namespace TechbloxModdingAPI /// public bool Static { - get => false; - set { } + get => BlockEngine.GetBlockInfo(this).isStatic; + set => BlockEngine.GetBlockInfo(this).isStatic = value; } /// diff --git a/TechbloxModdingAPI/Blocks/BlockTests.cs b/TechbloxModdingAPI/Blocks/BlockTests.cs index f920631..198011c 100644 --- a/TechbloxModdingAPI/Blocks/BlockTests.cs +++ b/TechbloxModdingAPI/Blocks/BlockTests.cs @@ -46,16 +46,20 @@ namespace TechbloxModdingAPI.Blocks "Block ID enum matches the known block types."); } + private static Block[] blocks; // Store placed blocks as some blocks are already present as the workshop and the game save [APITestCase(TestType.EditMode)] public static void TestBlockIDs() { float3 pos = new float3(); - foreach (BlockIDs id in Enum.GetValues(typeof(BlockIDs))) + var values = Enum.GetValues(typeof(BlockIDs)); + blocks = new Block[values.Length - 1]; // Minus the invalid ID + int i = 0; + foreach (BlockIDs id in values) { if (id == BlockIDs.Invalid) continue; try { - Block.PlaceNew(id, pos); + blocks[i++] = Block.PlaceNew(id, pos); pos += 0.2f; } catch (Exception e) @@ -71,8 +75,9 @@ namespace TechbloxModdingAPI.Blocks [APITestCase(TestType.EditMode)] public static IEnumerator TestBlockProperties() { //Uses the result of the previous test case - var blocks = Game.CurrentGame().GetBlocksInGame(); yield return Yield.It; + if (blocks is null) + yield break; for (var index = 0; index < blocks.Length; index++) { if (index % 50 == 0) yield return Yield.It; //The material or flipped status can only be changed 130 times per submission @@ -80,6 +85,7 @@ namespace TechbloxModdingAPI.Blocks if (!block.Exists) continue; foreach (var property in block.GetType().GetProperties()) { + if (property.Name == "Material" || property.Name == "Flipped") continue; // TODO: Crashes in game //Includes specialised block properties if (property.SetMethod == null) continue; var testValues = new (Type, object, Predicate)[] @@ -121,8 +127,24 @@ namespace TechbloxModdingAPI.Blocks yield break; } - property.SetValue(block, valueToUse); - object got = property.GetValue(block); + try + { + property.SetValue(block, valueToUse); + } + catch (Exception e) + { + Assert.Fail($"Failed to set property {block.GetType().Name}.{property.Name} to {valueToUse}\n{e}"); + } + object got; + try + { + got = property.GetValue(block); + } + catch (Exception e) + { + Assert.Fail($"Failed to get property {block.GetType().Name}.{property.Name}\n{e}"); + continue; + } var attr = property.GetCustomAttribute(); if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got))) { diff --git a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs index 1557f53..75d0cc4 100644 --- a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs +++ b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs @@ -215,10 +215,10 @@ namespace TechbloxModdingAPI.Tests .Action(() => Player.LocalPlayer.GetBlockLookedAt().Static = true).Build(); Game.AddPersistentDebugInfo("InstalledMods", InstalledMods); - Block.Placed += (sender, args) => + /*Block.Placed += (sender, args) => Logging.MetaDebugLog("Placed block " + args.Block); Block.Removed += (sender, args) => - Logging.MetaDebugLog("Removed block " + args.Block); + Logging.MetaDebugLog("Removed block " + args.Block);*/ } // dependency test