I’ll close with a couple of general quality-of-life tips.
Workflow
Making a mod, like all codding, invariably involves trying things, seeing the effect, and then trying something else. In this loop it’s easy to forget to compile your changes in VS and, especially, forget to copy the new .dll from your working directory to the FF/Mono/Mod folder so that you can actually test the changes in the game. This last step can be automated by having some variant of
xcopy "$(TargetPath)" "[...]\Farthest Frontier\Farthest Frontier (Mono)\Mods\" /y
in Project Properties → Build Events → Post-build event command line.
Private fields
Because both MelonLoader and HarmonyPatch work from the “outside” of a class, it’s a very common occurrence in modding that you end up wanting to change a private / protected field, which the compiler forbids. For example, in the previous post we were lucky that ForagerShack.foragingRadius had a public setter. If it hadn’t we would have had to set the private field _foragingRadius manually. In c# it’s possible to use reflection to get around these restrictions. This is normally something that you should not do when codding, but in this case it’s inevitable.
The following are some util functions that automate this reflection, I have them in their own file that I share across all my mods because they are so useful so often:
public static object GetPrivateField<T>(string fieldName, T instance)
{
FieldInfo field = typeof(T).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
return field.GetValue(instance);
}
public static void SetPrivateField<T1, T2>(string fieldName, T1 instance, T2 value)
{
FieldInfo field = typeof(T1).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(instance, value);
}
public static object CallPrivateMethod<T>(string methodName, T instance, object[] args = null)
{
MethodInfo method = typeof(T).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
return method.Invoke(instance, args);
}