In order to get started with making your own modules in your build of Twinkie, you need to first understand some basic C++ class inheritance (including virtual functions). Once you understand that, you can go ahead and make a new .h file in the Modules directory.
IModule is the class that all modules inherit from, and your module is required to adhere to the specifications of IModule and inherit from it.
Let's try making a new module called MyModule:
#pragma once
#include "../IModule.h"
class MyModule : public IModule
{
}Now, we can use this module by adding it to the std::vector<IModule*> TwinkUi.Modules (after including our MyModule.h file):
Modules.push_back(new MyModule(TrackmaniaMgr, Logger, &DoRender));Now, how do we make this module do stuff?
Before implementing these functions, remember to make them public!
Render() is used to render ImGui/nanovg draw calls while the main UI is visible (assert(*UiRenderEnabled)).
RenderAnyways() renders regardless of the current UI visiblity state, but it only render if the module is active (assert(Enabled)).
RenderInactive() always renders, even if the module is inactive.
RenderSettings() is used to render the inside of the settings tab for the module.
RenderMenuItem() is called to render the Menu item used to enable/disable the module. Try only using ImGui::MenuItem or similar calls when implementing this function.
SettingsInit(SettingMgr&) is called when Twinkie is launched, to load the values in the Twinkie.ini file used to store modules' settings.
We will now get into loading and saving settings using SettingMgr.
.ini files are structured this way:
[My Module]
My String = hello guys
My Vec4 = 1.0,1.0,0.5,3.14159
My Bool = falsewhere all values are loaded as strings by default, however Twinkie is able to understand these as the datatypes you request.
To get the MyModule set of values from the above example .ini file:
virtual void SettingsInit(SettingMgr& Settings)
{
Tab& MyModuleTab = Settings["My Module"];
}The reason for naming the class Tab is for future purposes, where Tabs and Settings will be directly rendered instead of being managed by the module.
Now that we have the Tab ready for use, we can access our values:
Tab& MyModuleTab = Settings["My Module"];
Setting& MyStringSetting = MyModuleTab["My String"];Since it's a string, you can directly use the std::string Setting.Value to get the value you want.
Or if you don't want the possiblity of the string being empty by default, you can use the void Setting.GetAsString(std::string*) which will not overwrite the value living at the provided pointer if the string is empty.
Setting& MyVec4Setting = MyModuleTab["My Vec4"];
ImVec4 Vector4 = ImVec4(0.0, 1.0, 0.5, 0.0);
MyVec4Setting.GetAsVec4(&Vector4);Again, this will not overwrite the value of Vector4 if the setting does not exist/is an empty string.
You can use the Setting.Set(auto) function to set a setting to a specified value.
Settings["My Module"]["My Vec4"].Set(Vector4);Do keep in mind that the operator[] on the SettingMgr and Tab will create the Tab or Setting respectively if they don't exist at all.
There are some custom flags you can set to your module:
virtual bool IsDebug(): this makes allRenderMenuItemcalls render inside the Debug menu item in the bar.virtual bool HasSettings(): this makes the module not have a tab at all in the settings menu.