-
-
Notifications
You must be signed in to change notification settings - Fork 32
For Developers
Have you ever wanted to add a counter of your own to Counters+, but you want it to also be its own standalone mod? Counters+ has a solution that allows you to easily implement any Counter of choice into the Counters+ system, allowing people to easily reposition them as if it was part of Counters+.
Because this page might be long, here are some quick links to the various ways you can contribute to Counters+.
The Custom Counter system is a way to store a reference to another mod's GameObject in the Counters+ config, along with basic credits and other settings information, to allow a user to easily reconfigure the positioning of an outside counter as if it was part of Counters+.
Developers who create UI Enhancement mods that take up part of the in-game UI space should probably look into Counters+ integration, as users may have setups that will collide with your mod.
If you'd much rather not read a short tutorial, and want to look at some actual code, here are links to some GitHub projects that take advantage of Counters+'s Custom Counter system.
To begin adding your counter to the Counters+ system, begin by adding Counters+ as a reference to your project, and check if Counters+ is installed (Preferably, in the Init
or OnApplicationStart
function of your Plugin.cs
class.
using IPA.Loader; //Add References from Beat Saber_Data/Managed
public class Plugin : IBeatSaberPlugin {
public void Init(){
if (PluginManager.GetPlugin("Counters+")) {
AddCustomCounter();
}
}
//...
}
To prevent unwanted exceptions, put your Custom Counter creation code into its own separate function!
Next, reference the CountersPlus.Custom namespace (using CountersPlus.Custom;
), and create a CustomCounter object.
- SectionName is used as an identifier in the Counters+ config. Make sure it is unique! Do not plan on changing this once you have released your mod with Custom Counter support.
- Name is the display name to be displayed in the Counters+ Settings Menu. It is recommended to omit "Counter" from the name.
-
BSIPAMod/Mod are references to the plugin that created it. Use one or the other depending on if your plugin is a BSIPA plugin (Inherits
IBeatSaberPlugin
), or an IPA Reloaded plugin (InheritsIPlugin
). -
Counter is the name of the GameObject that contains your Counter. Make sure any and all UI components you have are parented to a
Canvas
component that is on this GameObject. You can only have 1 GameObject per Custom Counter!
Finally, you can call CustomCounterCreator.Create()
to create your Custom Counter.
using IPA.Loader;
using CountersPlus.Custom;
public class Plugin : IBeatSaberPlugin {
//...
private void AddCustomCounter() {
CustomCounter counter = new CustomCounter {
SectionName = "testCounter",
Name = "Test",
BSIPAMod = this,
Counter = "testCounterGameObject",
};
CustomCounterCreator.Create(counter);
}
}
And that's it! If it has not yet been created, Counters+ will append the custom counter to its CountersPlus.ini
file, and will go off of that from now on. Your Counter can now be subject to the same base configuration settings as every counter!
Currently, the default settings for every custom counter is Below Combo, with a Distance of 2. If you instead want your Counter to appear someplace else, Counters+ has a way for you to attach defaults with your custom counter.
Simply create a CustomConfigModel
object (with the name as the parameter), edit your default settings, and then input those defaults along with the counter in CustomCounterCreator.Create()
using IPA.Loader;
using CountersPlus.Custom;
public class Plugin : IBeatSaberPlugin {
//...
private void AddCustomCounter() {
//...
CustomConfigModel defaults = new CustomConfigModel(counter.Name);
defaults.Enabled = true;
defaults.Position = CountersPlus.Config.ICounterPositions.AboveCombo;
defaults.Distance = 0;
CustomCounterCreator.Create(counter, defaults);
}
}
When your Custom Counter is created for the first time, Counters+ will use your CustomConfigModel
as defaults when saving to config.
Perhaps, maybe your counter is too wide to fit above/below the Combo and Multiplier. Maybe you want the users to only place it above or below the highway and track?
Counters+ also has a way for you to restrict where the user can position your counter. Just supply different ICounterPositions
objects in CustomCounterCreator.Create()
, and those will be the only options available to the user when editing your counter.
using IPA.Loader;
using CountersPlus.Custom;
using CountersPlus.Config; //ICounterPositions is in here.
public class Plugin : IBeatSaberPlugin {
//...
private void AddCustomCounter() {
//...
CustomCounterCreator.Create(counter, defaults, ICounterPositions.AboveHighway, ICounterPositions.BelowEnergy);
}
}
...It really is as simple as that! If you do not input any restricted positions, the counter will instead be able to use all 6.
The main reason for the Custom Counter system is to prevent mods from having to depend on Counters+ in order to function. Custom Counters is more of a bonus if the user also happens to have Counters+ installed.
Along with this, any mod developers who happen to develop UI Enhancement mods will no longer have to worry about interfering with Counters+'s UI as they can integrate their own UI elements and have them become a part of the Counters+ system.
If you feel that an existing Counters+ counter needs a new feature, or option, then it might be worth forking Counters+ for yourself and looking into adding this yourself. This short section will cover the 3 main components you need to cover in order to add a new option to Counters+, complete with UI support.
We'll assume that you've already forked Counters+, got the project set up, and fixed reference issues (if any) and post-build events (Check that; I have a post build event for easier debugging for myself)
The first thing you should do when adding a new Counters+ option is to add it to an existing ConfigModel.
A ConfigModel
is an abstract class located in Config.cs
which holds configuration data for every counter in Counters+. Each Counter in Counters+ have an inherited ConfigModel class of their own, also located in the bottom of Config.cs
. Find the ConfigModel class of the Counter you wish to add an option to, and add it. Do not add to the ConfigModel
class itself, as you'll find that option for every single Counter in Counters+. Unless you want to.
With your new Option in hand, go into the Counters
folder of the Counters+ solution and modify the counter class you want with that new option. Most, if not all, of the Counters have a settings
variable, which you can easily use to access your new setting.
Now it's time to add your setting to the Counters+ Settings UI. For every counter, their advanced settings (Options that are outside of what the abstract ConfigModel
class contains) are located in AdvancedCounterSettings.cs
, in the UI
folder.
Holy blob of lambda expressions, batman!
Sorry, that's my fault. Didn't expect anyone to make settings themselves, but someone did, and that's why I'm writing this. This is all one giant Dictionary of lambda expressions, mapped to ConfigModels. Given some time to figure out the layout, you can easily scroll down to your config model of choice, and copy and paste code from the surrounding lambda expressions to create your UI setting of choice.
If you feel your new option deserves being reflected in the display, go ahead and add it to the MockCounter.cs
class.
A Mock Counter is a counter that doesn't display actual data, it's only purpose is to give the user an idea of how it might look in-game.
If you notice, MockCounter.cs
is another mess. Again, I didn't plan on someone going as far as to make a new option. You'll find your way around it eventually!
If you've made it this far, congrats! A new option has been successfully added to an existing Counters+ counter. Good job! Go ahead and add your name and what you did to the ContributorsAndDonators.cs
class, and you will show up in the Contributors list.
Once you're done, shoot a pull request to the master branch, and I'll look over it and see if it's good enough to merge into master.