@@ -4,14 +4,18 @@ Proof of concept mod and reference implementation | |||
## Installation | |||
1. Patch Gamecraft with [IPA](https://github.com/Eusth/IPA). | |||
You will need to build it yourself; the latest release does not work (in the future the latest build will be available for download) | |||
2. Build TestMod solution and copy `obj\Debug\net45\TestMod.dll` into Gamecraft's `Plugins\` folder (IPA should have created this automatically in the previous step) | |||
1. Patch Gamecraft with [GCIPA](https://git.exmods.org/modtainers/GCIPA). | |||
You should download the latest release and extract it to the Gamecraft folder. | |||
To patch, drag `Gamecraft.exe` onto `IPA.exe`. You will need to redo this step whenever Gamecraft is updated. | |||
2. Extract the TestMod zip into Gamecraft's `Plugins\` folder (GCIPA should have created this automatically in the previous step). You should see `0Harmony.dll` and `TestMod.dll` in the `Plugins\` folder. If those files are in another folder in `Plugins\` it will not work. | |||
3. Launch Gamecraft. | |||
You can check the log file `%APPDATA%\..\LocalLow\FreeJam\RobocraftX\Player.log` to confirm | |||
You can check the log file `%APPDATA%\..\LocalLow\FreeJam\RobocraftX\Player.log` to confirm. | |||
You should be able to see a message near the top showing how many plugins (should be 1) have been loaded and their names (should be TestPlugin v0.0.0a). | |||
## Development | |||
Interested in making your own mod? | |||
Clone this repository and modify the Plugin class in `TestMod\TestPlugin.cs`. | |||
Patch Gamecraft with [GCIPA](#installation) | |||
Build the solution and copy `bin\Debug\net45\TestMod.dll` and `bin\Debug\net45\0Harmony.dll` into Gamecraft's `Plugins\` folder. | |||
More information about the IPlugin and IEnhancedPlugin interface can be found [on the IPA repository](https://github.com/Eusth/IPA) |
@@ -13,9 +13,17 @@ namespace TestMod.Waypoints | |||
{ | |||
static TeleportCharacterCommandEngine Instance {get; private set;} | |||
static void Postfix(TeleportCharacterCommandEngine __result) | |||
static void Postfix(TeleportCharacterCommandEngine __result, TeleportCharacterCommandEngine __instance) | |||
{ | |||
Instance = __result; | |||
// this works because this class should be a singleton; only one copy of it should exist at a time | |||
if (__instance != null) | |||
{ | |||
Instance = __instance; | |||
Debug.Log("Instance caught from constructor"); | |||
} else { | |||
Instance = __result; | |||
Debug.Log("Result caught from constructor") | |||
} | |||
Debug.Log("Caught TeleportCharacterCommandEngine"); | |||
} | |||
} | |||
@@ -3,14 +3,20 @@ using System.Dictionary.Generic; | |||
using Harmony; | |||
using UnityEngine; | |||
using Unity.Mathematics; | |||
using RobocraftX.Character; | |||
using RobocraftX.StateSync; | |||
using RobocraftX.GUI.CommandLine; | |||
using uREPL; | |||
namespace TestMod.Waypoints | |||
{ | |||
[HarmonyPatch] | |||
[HarmonyPatch(typeof(uREPL.RuntimeCommands))] | |||
[HarmonyPatch("Register")] | |||
[HarmonyPatch(new Type[] { typeof(string), typeof(Action<float,float,float>), typeof(string) })] | |||
class TeleportPatch | |||
{ | |||
// TeleportPatch intercepts all Register(string, Action<float,float,float> string) method calls | |||
// which allows it to catch Teleport Actions as they're registered as CLI commands | |||
static Action<float,float,float> TeleportAbsolute {get; private set;} | |||
static Action<float, float, float> TeleportRelative {get; private set;} | |||
@@ -19,7 +25,7 @@ namespace TestMod.Waypoints | |||
static void Prefix(string name, Action func, string description) | |||
{ | |||
Debug.Log("Caught Register for "+name); | |||
Debug.Log("Caught Register method call for "+name); | |||
if (name.Equals("TeleportPlayerAbsolute")) | |||
{ | |||
TeleportAbsolute = func; | |||
@@ -31,13 +37,7 @@ namespace TestMod.Waypoints | |||
} | |||
} | |||
static MethodBase TargetMethod(HarmonyInstance instance) | |||
{ | |||
Type CLUtility = AccessTools.TypeByName("RobocraftX.GUI.CommandLine.CommandLineUtility"); | |||
return Harmony.AccessTools.Method(CLUtility, "Register", new Type[]{ typeof(string), typeof(Action<float, float, float>), typeof(string) }, new Type[]{ typeof(float), typeof(float), typeof(float) }); | |||
} | |||
static void WaypointCommand(object name) | |||
static void WaypointHereCommand(object name) | |||
{ | |||
// hopefully entitiesDB does not become private in the future... | |||
if (TeleportEnginePatch.Instance == null) | |||
@@ -45,16 +45,16 @@ namespace TestMod.Waypoints | |||
uREPL.Log.Error("Teleport command object missing!"); | |||
return; | |||
} | |||
ref RigidBodyEntityStruct reference = ref TeleportEnginePatch.Instance.entitiesDB.QueryEntity<RigidBodyEntityStruct>(0u, CharacterExclusiveGroups.CharacterGroup); | |||
if (_waypoints.ContainsKey(name)) | |||
{ | |||
_waypoints.Remove(name); | |||
} | |||
ref RigidBodyEntityStruct reference = ref TeleportEnginePatch.Instance.entitiesDB.QueryEntity<RigidBodyEntityStruct>(0u, CharacterExclusiveGroups.CharacterGroup); | |||
_waypoints.Add(new float[3]{reference.x, reference.y, reference.z}); | |||
uREPL.Log.Output("Saved "+name.ToString()); | |||
} | |||
static void TeleportWaypointCommand(object name) | |||
static void TeleportToWaypointCommand(object name) | |||
{ | |||
if (!_waypoints.ContainsKey(name)) | |||
{ | |||
@@ -66,7 +66,7 @@ namespace TestMod.Waypoints | |||
uREPL.Log.Error("TeleportPlayerAbsolute command missing!"); | |||
return; | |||
} | |||
TeleportAbsolute(_waypoints[name]); | |||
TeleportAbsolute(_waypoints[name][0], _waypoints[name][1], _waypoints[name][2]); | |||
uREPL.Log.Output("Teleported player to "+name.ToString()); | |||
} | |||
} | |||
@@ -37,6 +37,11 @@ namespace TestMod | |||
commandHelp.Invoke(null, new string[] { "Echo", "Write a message to the command line" }); | |||
uREPL.RuntimeCommands.Register("Exit", QuitCommand, "Close Gamecraft"); | |||
commandHelp.Invoke(null, new string[] { "Exit", "Forcefully exit Gamecraft immediately" }); | |||
// waypoint commands | |||
uREPL.RuntimeCommands.Register<object>("WaypointHere", Waypoints.WaypointHereCommand, "/shrug"); | |||
commandHelp.Invoke(null, new string[] { "WaypointHere", "Create a named waypoint in your current location" }); | |||
uREPL.RuntimeCommands.Register<object>("TeleportToWaypoint", Waypoints.TeleportToWaypoint, "Blame Josh"); | |||
commandHelp.Invoke(null, new string[] { "TeleportToWaypoint", "Teleport to a waypoint by name" }); | |||
Debug.Log("TestPlugin startup complete"); | |||
} | |||