A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
9.8KB

  1. using System;
  2. using System.Diagnostics;
  3. using System.Reflection;
  4. using System.Text;
  5. using TechbloxModdingAPI.App;
  6. using HarmonyLib;
  7. using IllusionInjector;
  8. // test
  9. using RobocraftX.FrontEnd;
  10. using Unity.Mathematics;
  11. using UnityEngine;
  12. using RobocraftX.Common.Input;
  13. using TechbloxModdingAPI.Blocks;
  14. using TechbloxModdingAPI.Commands;
  15. using TechbloxModdingAPI.Input;
  16. using TechbloxModdingAPI.Interface.IMGUI;
  17. using TechbloxModdingAPI.Players;
  18. using TechbloxModdingAPI.Utility;
  19. namespace TechbloxModdingAPI.Tests
  20. {
  21. #if DEBUG
  22. // unused by design
  23. /// <summary>
  24. /// Modding API implemented as a standalone IPA Plugin.
  25. /// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself
  26. /// </summary>
  27. public class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin
  28. {
  29. private static Harmony harmony { get; set; }
  30. public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
  31. public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
  32. public string HarmonyID { get; } = "org.git.exmods.modtainers.techbloxmoddingapi";
  33. public override void OnApplicationQuit()
  34. {
  35. Main.Shutdown();
  36. }
  37. public override void OnApplicationStart()
  38. {
  39. FileLog.Reset();
  40. Harmony.DEBUG = true;
  41. Main.Init();
  42. Logging.MetaDebugLog($"Version group id {(uint)ApiExclusiveGroups.versionGroup}");
  43. // disable background music
  44. Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers()));
  45. //AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :(
  46. //Utility.VersionTracking.Enable();//(very) unstable
  47. // debug/test handlers
  48. Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
  49. // debug/test commands
  50. if (Dependency.Hell("ExtraCommands"))
  51. {
  52. CommandBuilder.Builder()
  53. .Name("Exit")
  54. .Description("Close Techblox immediately, without any prompts")
  55. .Action(() => { UnityEngine.Application.Quit(); })
  56. .Build();
  57. CommandBuilder.Builder()
  58. .Name("SetFOV")
  59. .Description("Set the player camera's field of view")
  60. .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
  61. .Build();
  62. CommandBuilder.Builder()
  63. .Name("MoveLastBlock")
  64. .Description("Move the most-recently-placed block, and any connected blocks by the given offset")
  65. .Action((float x, float y, float z) =>
  66. {
  67. if (GameState.IsBuildMode())
  68. foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
  69. block.Position += new Unity.Mathematics.float3(x, y, z);
  70. else
  71. Logging.CommandLogError("Blocks can only be moved in Build mode!");
  72. }).Build();
  73. CommandBuilder.Builder()
  74. .Name("PlaceAluminium")
  75. .Description("Place a block of aluminium at the given coordinates")
  76. .Action((float x, float y, float z) =>
  77. {
  78. var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z));
  79. Logging.CommandLog("Block placed with type: " + block.Type);
  80. })
  81. .Build();
  82. CommandBuilder.Builder()
  83. .Name("PlaceAluminiumLots")
  84. .Description("Place a lot of blocks of aluminium at the given coordinates")
  85. .Action((float x, float y, float z) =>
  86. {
  87. Logging.CommandLog("Starting...");
  88. var sw = Stopwatch.StartNew();
  89. for (int i = 0; i < 100; i++)
  90. for (int j = 0; j < 100; j++)
  91. Block.PlaceNew(BlockIDs.Cube, new float3(x + i, y, z + j));
  92. sw.Stop();
  93. Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
  94. })
  95. .Build();
  96. Block b = null;
  97. CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up")
  98. .Action(() =>
  99. {
  100. if (b == null)
  101. {
  102. b = new Player(PlayerType.Local).GetBlockLookedAt();
  103. Logging.CommandLog("Block saved: " + b);
  104. }
  105. else
  106. Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0)));
  107. }).Build();
  108. CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
  109. .Action(() => { throw new Exception("Error Command always throws an error"); })
  110. .Build();
  111. CommandBuilder.Builder("ColorBlock",
  112. "Change color of the block looked at if there's any.")
  113. .Action<string>(str =>
  114. {
  115. if (!Enum.TryParse(str, out BlockColors color))
  116. {
  117. Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values.");
  118. var s = str.Split(' ');
  119. new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]),
  120. float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3]));
  121. return;
  122. }
  123. new Player(PlayerType.Local).GetBlockLookedAt().Color = color;
  124. Logging.CommandLog("Colored block to " + color);
  125. }).Build();
  126. CommandBuilder.Builder("MoveBlockByID", "Gets a block based on its object identifier and teleports it up.")
  127. .Action<char>(ch =>
  128. {
  129. foreach (var body in SimBody.GetFromObjectID(ch))
  130. {
  131. Logging.CommandLog("SimBody: " + body);
  132. body.Position += new float3(0, 10, 0);
  133. foreach (var bodyConnectedBody in body.GetConnectedBodies())
  134. {
  135. Logging.CommandLog("Moving " + bodyConnectedBody);
  136. bodyConnectedBody.Position += new float3(0, 10, 0);
  137. }
  138. }
  139. }).Build();
  140. CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.")
  141. .Action((float val, float max) =>
  142. {
  143. var body = new Player(PlayerType.Local).GetSimBodyLookedAt();
  144. if (body == null) return;
  145. body.CurrentHealth = val;
  146. body.InitialHealth = max;
  147. Logging.CommandLog("Health set to: " + val);
  148. }).Build();
  149. CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group")
  150. .Action((float x, float y, float z) =>
  151. {
  152. var pos = new float3(x, y, z);
  153. var group = BlockGroup.Create(new Block(BlockIDs.Cube, pos) {Color = BlockColors.Aqua});
  154. new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
  155. {Color = BlockColors.Blue, BlockGroup = group};
  156. new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
  157. {Color = BlockColors.Green, BlockGroup = group};
  158. new Block(BlockIDs.Cube, pos + new float3(1, 0, 0))
  159. {Color = BlockColors.Lime, BlockGroup = group};
  160. }).Build();
  161. CommandBuilder.Builder("placeCustomBlock", "Places a custom block, needs a custom catalog and assets.")
  162. .Action((float x, float y, float z) =>
  163. {
  164. Logging.CommandLog("Block placed: " +
  165. Block.PlaceNew((BlockIDs) 500, new float3(0, 0, 0)));
  166. }).Build();
  167. CommandBuilder.Builder("toggleTimeMode", "Enters or exits simulation.")
  168. .Action((float x, float y, float z) =>
  169. {
  170. Game.CurrentGame().ToggleTimeMode();
  171. }).Build();
  172. GameClient.SetDebugInfo("InstalledMods", InstalledMods);
  173. Block.Placed += (sender, args) =>
  174. Logging.MetaDebugLog("Placed block " + args.Block);
  175. Block.Removed += (sender, args) =>
  176. Logging.MetaDebugLog("Removed block " + args.Block);
  177. }
  178. // dependency test
  179. if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0")))
  180. {
  181. Logging.LogWarning("You're in TechbloxScripting dependency hell");
  182. }
  183. else
  184. {
  185. Logging.Log("Compatible TechbloxScripting detected");
  186. }
  187. // Interface test
  188. /*Group uiGroup = new Group(new Rect(20, 20, 200, 500), "TechbloxModdingAPI_UITestGroup", true);
  189. var button = new Button("TEST");
  190. button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
  191. var button2 = new Button("TEST2");
  192. button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
  193. Text uiText = new Text("This is text!", multiline: true);
  194. uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); };
  195. Label uiLabel = new Label("Label!");
  196. Image uiImg = new Image(name:"Behold this texture!");
  197. uiImg.Enabled = false;
  198. uiGroup.AddElement(button);
  199. uiGroup.AddElement(button2);
  200. uiGroup.AddElement(uiText);
  201. uiGroup.AddElement(uiLabel);
  202. uiGroup.AddElement(uiImg);*/
  203. /*Addressables.LoadAssetAsync<Texture2D>("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg")
  204. .Completed +=
  205. handle =>
  206. {
  207. uiImg.Texture = handle.Result;
  208. uiImg.Enabled = true;
  209. Logging.MetaDebugLog($"Got blue bg asset {handle.Result}");
  210. };*/
  211. /*((FasterList<GuiInputMap.GuiInputMapElement>)AccessTools.Property(typeof(GuiInputMap), "GuiInputsButtonDown").GetValue(null))
  212. .Add(new GuiInputMap.GuiInputMapElement(RewiredConsts.Action.ToggleCommandLine, GuiIn))*/
  213. #if TEST
  214. TestRoot.RunTests();
  215. #endif
  216. }
  217. private string modsString;
  218. private string InstalledMods()
  219. {
  220. if (modsString != null) return modsString;
  221. StringBuilder sb = new StringBuilder("Installed mods:");
  222. foreach (var plugin in PluginManager.Plugins)
  223. sb.Append("\n" + plugin.Name + " - " + plugin.Version);
  224. return modsString = sb.ToString();
  225. }
  226. public override void OnUpdate()
  227. {
  228. if (UnityEngine.Input.GetKeyDown(KeyCode.End))
  229. {
  230. Console.WriteLine("Pressed button to toggle console");
  231. FakeInput.CustomInput(new LocalInputEntityStruct {commandLineToggleInput = true});
  232. }
  233. }
  234. [HarmonyPatch]
  235. public class MinimumSpecsPatch
  236. {
  237. public static bool Prefix()
  238. {
  239. return false;
  240. }
  241. public static MethodInfo TargetMethod()
  242. {
  243. return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method;
  244. }
  245. }
  246. }
  247. #endif
  248. }