Due to popular request, I’ll be giving a brief tutorial on creating keybinds to toggle a modification.
There are multiple ways to implement keybinds, but I will use the method I personally find the easiest. To start out, create a new Class Library (.NET Framework) project inside of Visual Studio for .NET framework version 4.5.2 or up. I will not be going over how to get Visual Studio if you don't already have it.
Add the original Assembly-CSharp.dll and the UnityEngine.CoreModules.dll from the \Managed\ folder to the references. To do this, right-click on references in the solution explorer tab and click add references. Inside the references manager, go under the browse tab and click browse. Navigate to the \Managed\ folder for 7DTD and select both the Assembly-CSharp.dll and the UnityEngine.CoreModule.dll. Click add and verify they are checked inside the browse tab of the references manager, then click ok.
Now rename the class1.cs file to MPGH.cs (or any other name). Open the class file (if you haven't already) and rename the namespace and the class name to match the file name.
Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MPGH
{
public class MPGH
{
}
}
I will now add a initialization method to the MPGH class that will be called from the Assembly-CSharp.dll later to start the new code. I will also create a new class extending the Unity's MonoBehavior. UnityEngine will need to be imported at top of the class file for MonoBehavior to register. In the new MonoBehavior class I will implement the Awake and Update methods that are used by MonoBehavior objects.
Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace MPGH {
public class MPGH {
public static void Init() {
}
}
public class Listener : MonoBehaviour {
/** The Awake method is part of Unity's MonoBehavior and is executed upon object creation. */
public void Awake() {
}
/** The Update method is part of Unity's MonoBehavior and is executed every tick. */
public void Update() {
}
}
}
To finish off the basic framework, I need to add a line of code to create the Listener MonoBehavior object when the initialization method is called. To do this, I will create a new UnityEngine game object and add the listener class as a component.
Now that the basic framework is finished, I can start implementing the keybinding. I will start by making a boolean variable to hold the status of mod and a getter method to retrieve it easily. I will initialize it to false inside the awake method. Inside the update method I will add a check to see if a key has been pressed using Unity's Input class. When the correct key is pressed, I want the status boolean to be toggled.
Class
using System;
using UnityEngine;
namespace MPGH {
public class MPGH {
public static void Init() {
new GameObject().AddComponent<Listener>();
}
}
public class Listener : MonoBehaviour {
private static Boolean isEnabled;
/** Status getter */
public static Boolean isModEnabled() {
return isEnabled;
}
/** The Awake method is part of Unity's MonoBehavior and is executed upon object creation. */
public void Awake() {
isEnabled = false;
}
/** The Update method is part of Unity's MonoBehavior and is executed every tick. */
public void Update() {
/** Keybind listener */
if (Input.GetKeyDown(KeyCode.Insert)) {
isEnabled = !isEnabled;
}
}
}
}
The listening key can be changed by using a different KeyCode. You can find all the different ones by looking at Unity's KeyCode enum.
If you want an indication that the mod has been toggled in-game, you can use 7DTD's tooltips. I added a call to the tooltip method in GameManager and create a method to convert the boolean status into words. An example of this can be seen here:
Class
using System;
using UnityEngine;
namespace MPGH {
public class MPGH {
public static void Init() {
new GameObject().AddComponent<Listener>();
}
}
public class Listener : MonoBehaviour {
private static Boolean isEnabled;
/** Status getter */
public static Boolean isModEnabled() {
return isEnabled;
}
/** Get friendly status text */
private static String GetEnabledText() {
return isModEnabled() ? "enabled" : "disabled";
}
/** The Awake method is part of Unity's MonoBehavior and is executed upon object creation. */
public void Awake() {
isEnabled = false;
}
/** The Update method is part of Unity's MonoBehavior and is executed every tick. */
public void Update() {
/** Keybind listener */
if (Input.GetKeyDown(KeyCode.Insert)) {
isEnabled = !isEnabled;
GameManager.ShowTooltip(GameManager.Instance.World.GetPrimaryPlayer(), "the mod is now " + GetEnabledText());
}
}
}
}
Now build the project by right-clicking the project in the solution explorer and clicking build. If everything is working correctly, the MPGH.dll will be outputted into the bin folder in the project directory.
You can either drop this new .dll file into \Managed\ folder for 7DTD or you can use an utility like ILMerge and combine both the .dll assemblies together into a single one.
OPTIONAL:
To use Microsoft's ILMerge utility (which you can get from their download center), you need to start either a command prompt or powershell window and execute a command like the following: "ilmerge.exe /target:library /out:Assembly-CSharp.dll "E:\Programs\Steam\steamapps\common\7 Days To Die\7DaysToDie_Data\Managed\Assembly-CSharp.dll" "I:\Projects\Durata\MPGH\MPGH\bin\Release\MPGH.dll ". This will combine the Assembly-CSharp.dll from the \Managed\ directory with the MPGH.dll.
Now that we have our custom listener, all we need to do now is implement it into the Assembly-CSharp.dll. Open the Assembly-CSharp.dll with ILSpy and navigate to -/GameManeger/Awake(): Void method. At the very top of the reflexil menu, I will add a call the the MPGH/MPGH/Init() method I created (method reference).
The final step is to implement the check for the modification to see if it's enabled. In this example I will be making the mine detonation mod toggleable. In the -/BlockMine/OnEntityWalking(WorldBase, Int32, Int32, Int32, BlockValue, Entity): Void method, at the very top, I will be adding three opcodes, the first is a call to the MPGH/Listener/isModEnabled(): Boolean method in the Listener class (operand type of method reference). Then I will create a check to see if the mod is enabled by adding brfalse.s (instruction reference to the first opcode of the original method). Then after the check, I will add my return value. In this case, it's simply ret.
Annnnnnd I'm done! Save the assembly to the \Managed\ folder and toggle stuff!
I was pretty lazy creating this tutorial, so I glossed over some things. Regardless, I hope it still helps!
Best,
Sqeegie