A simple, real-world example to show how to set up your mod to use Mod Settings using annotated redscript code.
// Uses a module for all the usual reasons; no need to call my classes names like V1ldSettings
module V1ld.DontDisassembleExpensiveJunk
// Settings class must derive from ScriptableSystem which then creates and manages the singleton instance used by Mod Settings
class Settings extends ScriptableSystem {
// Define your mod's settings, annotated by Mod Settings' properties
@runtimeProperty("ModSettings.mod", "Don't Disassemble Expensive Junk")
@runtimeProperty("ModSettings.displayName", "Enable mod")
@runtimeProperty("ModSettings.description", "Enable or disable the mod.")
let enabled: Bool = true;
@runtimeProperty("ModSettings.mod", "Don't Disassemble Expensive Junk")
@runtimeProperty("ModSettings.category", "Features")
@runtimeProperty("ModSettings.category.order", "1")
@runtimeProperty("ModSettings.displayName", "Cutoff price")
@runtimeProperty("ModSettings.description", "All items cheaper than this will be disassembled.")
@runtimeProperty("ModSettings.step", "10")
@runtimeProperty("ModSettings.min", "10")
@runtimeProperty("ModSettings.max", "5000")
@runtimeProperty("ModSettings.dependency", "enabled")
let cutoffPrice: Int32 = 50;
// A simple helper method used by the mod
public final func ShouldDisassembleItem(player: ref<PlayerPuppet>, itemID: ItemID) -> Bool {
return this.enabled && RPGManager.CalculateSellPrice(player.GetGame(), player, itemID) < this.cutoffPrice;
}
// Mod Settings helpers & listeners
// A convenience getter for the singleton managed by ScriptableSystem
public static func Get(gi: GameInstance) -> ref<Settings> {
// Must use the fully qualified name for this class here: V1ld.DontDisassembleExpensiveJunk.Settings
return GameInstance.GetScriptableSystemsContainer(gi).Get(n"V1ld.DontDisassembleExpensiveJunk.Settings") as Settings;
}
// The OnAttach()/OnDetach() methods of ScriptableSystem are called when a save is loaded and unloaded.
//
// We use redscript's @if(ModuleExists()) conditional inclusion here to let the mod work even if the player
// hasn't installed Mod Settings. The @runtimeProperty declarations above will also be simply ignored if
// Mod Settings is not installed. The only difference will be that settings can't be changed, the mod
// will use the default setting defined above.
@if(ModuleExists("ModSettingsModule"))
private func OnAttach() -> Void { ModSettings.RegisterListenerToClass(this); }
@if(ModuleExists("ModSettingsModule"))
private func OnDetach() -> Void { ModSettings.RegisterListenerToClass(this); }
}That's it! Your mod is now set up to use Mod Settings and to have its settings values continuously updated as players make changes to their settings.
Here's an example of how to use the above in your mod. This is in the same file as above in my case, but doesn't really matter.
@replaceMethod(BackpackMainGameController)
protected cb func OnDisassembleJunkPopupClosed(data: ref<inkGameNotificationData>) -> Bool {
// [...]
while i < limit {
// Here we fetch a reference to our settings singleton. Note we can use just "Settings" instead of
// the fully module qualified name. This works because we're still in the same file and still within
// scope of the module declaration at the top. If this was another file, we'd need to use the fully
// qualified name instead V1ld.DontDisassembleExpensiveJunk.Settings.Get()
let cutoff = Settings.Get(this.m_player.GetGame());
// And here we use it. it's that simple.
//
// You can directly access cutoff.enabled or cutoff.cutoffPrice here too, if you prefer.
if cutoff.ShouldDisassembleItem(this.m_player, this.m_junkItems[i].GetID()) {
ItemActionsHelper.DisassembleItem(this.m_player, this.m_junkItems[i].GetID(), this.m_junkItems[i].GetQuantity());
}
i += 1;
}
// [...]
}