Skip to content

Mod Creation: First Mod

TheTimeSweeper edited this page Oct 5, 2023 · 8 revisions

The very basics.

What you'll need

  • Wizard of Legend
  • Visual Studio Community
    • when you install, make sure you have the .net 3.5 component
  • Any knowledge of c# (or even just willingness to learn)

Building the mod

We're going to start with a simple template for you to be able to start modding and build an example mod.

Let's do it baybee

  1. Download or clone this repo anywhere on your computer.
  2. Open the WolModExample.csproj in visual studio (now's the time to download it if you don't have it).
  3. At the top, go to Build > Build WolModExample.

And we're good! The you have just built the mod. You can now add the mod to your game:

  1. Navigate to Plugin-Example-Guide/WolModExample/bin/Debug/ and find your WolModExample.dll.
  2. Add that to your BepInEx/plugins folder and you can run the game with the mod.
  3. In game, press F1 and F2 to zoom in/out the camera, and if you have a second controller handy, go into coop mode and run around freely.

Alright what's going on

Let's take a deeper look at this project so know just what's needed to make a mod.

The Project Environment

  • If you've downloaded the template repo, you should be clear to open the project and skip to the next section. It's a working project environment you can use to create a mod.
  • To know how to set up a project from scratch, Follow this guide.

BaseUnityPlugin

The basis of every mod looks like this:

[BepInPlugin("CoolAuthorName.CoolModName", "Cool Mod Name", "0.1.0")]
public class MyCoolModPlugin : BaseUnityPlugin 
{
    void Awake() 
    {

    }
}

Let's go through this line by line and see what's happening.
(If you've cloned the template repo, you can also read the extensive comments in WolModExamplePlugin.cs.)

BepInPlugin Attribute

[BepInPlugin("CoolAuthorName.CoolModName", "Cool Mod Name", "0.1.0")]
  • [BepInPlugin]
    • This is an attribute that bepin will use to load our mod into the game
    • if you don't know what an attribute is, now's the time to learn some C#
  • "CoolAuthorName.CoolModName"
    • This is the identifier for your mod.
    • This one simply follows the convention of "AuthorName.YourModName".
  • "Cool Mod Name"
    • This is the human-readable name of your mod.
    • It can be literally anything, truthfully.
  • "0.1.0"
    • This is the version.
      • As you'd expect, useful for differing between updates.
    • This one follows the customary Semantic Versioning (major.minor.patch).
    • I usually keep it as 0.1.0 while developing, and put out a 1.0.0 when I feel the mod is complete.

Your Mod Class

public class MyCoolModPlugin : BaseUnityPlugin
  • BaseUnityPlugin is the main class that gets loaded by bepin.
  • It inherits from MonoBehaviour, so it gains all the familiar Unity callback functions you can use:
    • Awake, Start, Update, FixedUpdate, etc.

Awake

void Awake() 
{

}
  • Awake is the most important of the MonoBehaviour classes above.
    • It gets called by the game at the start of the class being initialized, i.e. as soon as your mod is loaded by bepin.
  • This is usually where you will write hooks (explained next), add things to things, change things, do whatever you like.

The Code

There are two main ways to write code in a bepin plugin. MMHooks, and HarmonyPatches. For simplicity, I'll just be going over MMHook here. You can read more about Harmony here

MMHook

Hooks are a powerful tool that lets us write code that executes alongside the code of the game.

To create a hook, it looks something like this:

On.someclass.somefunction += someclass_somefunction;
  • Start with the On. namespace, then go to the class and function you want to hook. Then type += and press the tab key. this will autofill the someclass_somefunction part, as well as create a function for the hook to run.
private void someclass_somefunction(someclass.orig_somefunction orig, someclass self) 
{
	//only nerds allowed past this point 
}
  • This function will have auto-filled parameters for the hook: the original function orig, and the original reference self.
    • orig is the original function of the game.
    • self is the reference to the instance of that original class.

Somewhere in the code you'll call orig(self).

private void someclass_somefunction(someclass.orig_somefunction orig, someclass self) 
{
	orig(self);
	//our code here.
}
  • This is very important. This runs the original code of the game.
  • With this you can simply decide to run your code before or after the original code runs.
  • Note that if you don't call orig(self), the original function (and any other mods that hook onto it) won't run, and your code will override it.

Let's run through an example:

private void CameraController_Awake(On.CameraController.orig_Awake orig, CameraController self) 
{
	orig(self);

	self.maxHorizontalDistBetweenPlayers = 100;
	self.maxVerticalDistBetweenPlayers = 100;
	//now we can run really far from each other!
}

In this example, we've hooked on to CameraController.Awake.

  • When that function is called, our hook function will also be called
  • We call orig(self) so the original code runs
  • Then we modify variables of the self instance.
    • So the self in that function refers to that particular CameraController that shows up in the game.
    • We simply mess with a few variables of that instance.

I believe that should get you started? Go forth and do amazing things!

  • Take a look at the overly commented WolModExamplePlugin.cs and see if you can follow what's going on.
    Absolutely get DNSpy if you haven't, so you can look at the game's code. (link stolen from ror2 modding wiki (but applies here of course))
  • If you'd like to make custom Relics (Items), Robes (Outfits) or Arcana (Skills), refer to the Custom Content pages on this wiki -->
  • If you have any questions go ahead and ask in the Wizard of Legend discord (#modding-extravaganza) and/or ping thetimesweeper he craves attention.

Also of course any and all feedback on this guide will be very appreciated. Thanks thanks have a lovely evening c: