Mod Creation/Mod Context API Reference: Difference between revisions
Buttchouda (talk | contribs) No edit summary |
Buttchouda (talk | contribs) No edit summary |
||
Line 1: | Line 1: | ||
== Accessing the Mod Context Object == | == Accessing the Mod Context Object == | ||
All examples in this guide will assume a mod context object ctx is in the current scope. | All examples in this guide will assume a mod context object ctx is in the current scope. | ||
=== From a Module === | === From a Module === | ||
If the module is defined as the <code>"setup"</code> for your mod in <code>manifest.json</code>, the exported <code>setup</code> function will receive the context object as a sole parameter: | If the module is defined as the <code>"setup"</code> for your mod in <code>manifest.json</code>, the exported <code>setup</code> function will receive the context object as a sole parameter: | ||
<nowiki>export function setup(ctx) { | <nowiki>export function setup(ctx) { | ||
// ... | // ... | ||
Line 9: | Line 12: | ||
Otherwise, use the global <code>mod.getContext</code> method, passing in your module's meta object: | Otherwise, use the global <code>mod.getContext</code> method, passing in your module's meta object: | ||
<nowiki>const ctx = mod.getContext(import.meta);</nowiki> | <nowiki>const ctx = mod.getContext(import.meta);</nowiki> | ||
=== From a Script === | === From a Script === | ||
The recommended approach for scripts is to use the global <code>mod.register</code> method. This only works in scripts injected via the <code>"load"</code> property of <code>manifest.json</code> or the <code>loadScript</code> method of the context object. | The recommended approach for scripts is to use the global <code>mod.register</code> method. This only works in scripts injected via the <code>"load"</code> property of <code>manifest.json</code> or the <code>loadScript</code> method of the context object. | ||
<nowiki>mod.register(ctx => { | <nowiki>mod.register(ctx => { | ||
// ... | // ... | ||
Line 18: | Line 24: | ||
=== From a Lifecycle Method === | === From a Lifecycle Method === | ||
All game lifecycle method callbacks will also receive their respective mod's context object as a sole parameter. | All game lifecycle method callbacks will also receive their respective mod's context object as a sole parameter. | ||
<nowiki>onCharacterLoaded(ctx => { | <nowiki>onCharacterLoaded(ctx => { | ||
// ... | // ... | ||
Line 24: | Line 32: | ||
=== From the Dev Context === | === From the Dev Context === | ||
For easier prototyping, you can use the global <code>mod.getDevContext</code> method to get a special dev mod context object. This should not be used in a mod, but only for development purposes (via the console). Any APIs that require resources will not work as the dev "mod" does not contain any resources. | For easier prototyping, you can use the global <code>mod.getDevContext</code> method to get a special dev mod context object. This should not be used in a mod, but only for development purposes (via the console). Any APIs that require resources will not work as the dev "mod" does not contain any resources. | ||
<nowiki>const devCtx = mod.getDevContext();</nowiki> | <nowiki>const devCtx = mod.getDevContext();</nowiki> | ||
== Loading Resources == | == Loading Resources == | ||
🚨 '''All resource file paths must be relative to the root of your mod''' 🚨 | 🚨 '''All resource file paths must be relative to the root of your mod''' 🚨 | ||
=== | === getResourceUrl(path: string): string === | ||
Retrieves a usable URL for any resource packaged in your mod. | Retrieves a usable URL for any resource packaged in your mod. | ||
Line 48: | Line 60: | ||
song.play();</nowiki> | song.play();</nowiki> | ||
=== | === loadModule(path: string): Promise<any> === | ||
Dynamically imports a JavaScript module. | Dynamically imports a JavaScript module. | ||
Line 60: | Line 73: | ||
'''Example''' | '''Example''' | ||
<nowiki>// my-module.mjs | <nowiki>// my-module.mjs | ||
export function greet(name) { | export function greet(name) { | ||
Line 68: | Line 82: | ||
myModule.greet('Melvor'); // Hello, Melvor!</nowiki> | myModule.greet('Melvor'); // Hello, Melvor!</nowiki> | ||
=== | === loadScript(path: string): Promise<void> === | ||
Injects a JavaScript file into the page. | Injects a JavaScript file into the page. | ||
Line 80: | Line 95: | ||
'''Example''' | '''Example''' | ||
<nowiki>// Await call if wanting the script to run before continuing | <nowiki>// Await call if wanting the script to run before continuing | ||
await ctx.loadScript('my-script.js'); | await ctx.loadScript('my-script.js'); | ||
Line 88: | Line 104: | ||
// my-independent-script.js has NOT run yet</nowiki> | // my-independent-script.js has NOT run yet</nowiki> | ||
=== | === loadTemplates(path: string): void === | ||
Inject all <template> elements contained in a given HTML file into the document body. | Inject all <template> elements contained in a given HTML file into the document body. | ||
Line 96: | Line 113: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.loadTemplates('my-templates.html');</nowiki> | <nowiki>ctx.loadTemplates('my-templates.html');</nowiki> | ||
=== | === loadStylesheet(path: string): void === | ||
Injects a CSS stylesheet into the page. | Injects a CSS stylesheet into the page. | ||
Line 106: | Line 125: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.loadStylesheet('my-styles.css');</nowiki> | <nowiki>ctx.loadStylesheet('my-styles.css');</nowiki> | ||
=== | === loadData(path: string): Promise<any> === | ||
Loads data from a JSON resource. | Loads data from a JSON resource. | ||
Line 120: | Line 141: | ||
'''Example''' | '''Example''' | ||
<nowiki>// my-data.json | <nowiki>// my-data.json | ||
{ | { | ||
Line 132: | Line 154: | ||
== Lifecycle Hooks == | == Lifecycle Hooks == | ||
=== | |||
=== onModsLoaded(callback: (ctx: ModContext) => void | Promise<void>): void === | |||
Execute code after all mods have been loaded (character select screen). | Execute code after all mods have been loaded (character select screen). | ||
Line 140: | Line 164: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.onModsLoaded(async (ctx) => { | <nowiki>ctx.onModsLoaded(async (ctx) => { | ||
// ... | // ... | ||
});</nowiki> | });</nowiki> | ||
=== | === onCharacterSelectionLoaded(callback: (ctx: ModContext) => void | Promise<void>): void === | ||
Execute code after the the character selection screen has fully loaded. | Execute code after the the character selection screen has fully loaded. | ||
Line 152: | Line 178: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.onCharacterSelectionLoaded(async (ctx) => { | <nowiki>ctx.onCharacterSelectionLoaded(async (ctx) => { | ||
// ... | // ... | ||
});</nowiki> | });</nowiki> | ||
=== | === onCharacterLoaded(callback: (ctx: ModContext) => void | Promise<void>): void === | ||
Execute code after the player's chosen character has loaded and all game objects are created, but before offline progress calculations. | Execute code after the player's chosen character has loaded and all game objects are created, but before offline progress calculations. | ||
Line 164: | Line 192: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.onCharacterLoaded(async (ctx) => { | <nowiki>ctx.onCharacterLoaded(async (ctx) => { | ||
// ... | // ... | ||
});</nowiki> | });</nowiki> | ||
=== | === onInterfaceReady(callback: (ctx: ModContext) => void | Promise<void>): void === | ||
Execute code after offline progress has been calculated and all in-game user interface elements have been created. | Execute code after offline progress has been calculated and all in-game user interface elements have been created. | ||
Line 176: | Line 206: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.onInterfaceReady(async (ctx) => { | <nowiki>ctx.onInterfaceReady(async (ctx) => { | ||
// ... | // ... | ||
Line 181: | Line 212: | ||
== Adding and Modifying Game Data == | == Adding and Modifying Game Data == | ||
✔️ '''Not documented yet, but it is available''' ✔️ | ✔️ '''Not documented yet, but it is available''' ✔️ | ||
== Mod Settings == | == Mod Settings == | ||
The mod settings API can be accessed through the <code>settings</code> property on the root context object. | The mod settings API can be accessed through the <code>settings</code> property on the root context object. | ||
=== | === section(name: string): Section === | ||
Gets or creates a settings section. The order that sections are created are the order they will display in a mod's settings window. | Gets or creates a settings section. The order that sections are created are the order they will display in a mod's settings window. | ||
Line 198: | Line 232: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.settings.section('General'); | <nowiki>ctx.settings.section('General'); | ||
ctx.settings.section('Other'); | ctx.settings.section('Other'); | ||
Line 204: | Line 239: | ||
// 2. Other</nowiki> | // 2. Other</nowiki> | ||
==== | ==== Section.add(config: SettingConfig | SettingConfig[]): void ==== | ||
Adds a setting to the section. The order that settings are added to a section are the order they will display in a mod's settings window. | Adds a setting to the section. The order that settings are added to a section are the order they will display in a mod's settings window. | ||
Line 212: | Line 248: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.settings.section('General').add({ | <nowiki>ctx.settings.section('General').add({ | ||
type: 'switch', | type: 'switch', | ||
Line 230: | Line 267: | ||
}]);</nowiki> | }]);</nowiki> | ||
==== | ==== Section.get(name: string): any ==== | ||
Gets the current value of a setting by its name property. | Gets the current value of a setting by its name property. | ||
Line 242: | Line 280: | ||
'''Example''' | '''Example''' | ||
<nowiki>// Assuming the player has typed "1" into the setting | <nowiki>// Assuming the player has typed "1" into the setting | ||
ctx.settings.section('Other').get('pick-a-number'); // 1</nowiki> | ctx.settings.section('Other').get('pick-a-number'); // 1</nowiki> | ||
==== | ==== Section.set(name: string, value: any): void ==== | ||
Programmatically sets the value of a setting by its name property. | Programmatically sets the value of a setting by its name property. | ||
Line 255: | Line 295: | ||
'''Example''' | '''Example''' | ||
<nowiki>ctx.settings.section('Other').set('pick-a-number', 5);</nowiki> | <nowiki>ctx.settings.section('Other').set('pick-a-number', 5);</nowiki> | ||
=== | === type(name: string, config: SettingTypeConfig): void === | ||
Registers a setting type that can then be used by by any mod when adding a setting. | Registers a setting type that can then be used by by any mod when adding a setting. | ||
Line 267: | Line 309: | ||
'''Example''' | '''Example''' | ||
<nowiki>// manifest.json | <nowiki>// manifest.json | ||
{ | { | ||
Line 283: | Line 326: | ||
Other mods will have to add your namespace to use your custom type: | Other mods will have to add your namespace to use your custom type: | ||
<nowiki>ctx.settings.section('Other').add({ | <nowiki>ctx.settings.section('Other').add({ | ||
type: 'my_mod:customText', | type: 'my_mod:customText', | ||
Line 288: | Line 332: | ||
});</nowiki> | });</nowiki> | ||
==== | ==== SettingTypeConfig ==== | ||
All functions are required. | All functions are required. | ||
===== | ===== render(name: string, onChange: () => void, config: SettingConfig): HTMLElement ===== | ||
The render function is responsible for using any properties passed into the config to render HTML for the setting. | The render function is responsible for using any properties passed into the config to render HTML for the setting. | ||
Line 301: | Line 347: | ||
Individual settings can opt to return validation errors in their <code>onChange</code> method. You can give a place to display this validation error in an element with a class of <code>validation-message</code>. | Individual settings can opt to return validation errors in their <code>onChange</code> method. You can give a place to display this validation error in an element with a class of <code>validation-message</code>. | ||
<nowiki>// The render function for a simple text box | <nowiki>// The render function for a simple text box | ||
function render(name, onChange, config) { | function render(name, onChange, config) { | ||
Line 329: | Line 376: | ||
}</nowiki> | }</nowiki> | ||
===== | ===== get(root: HTMLElement): any ===== | ||
The <code>get</code> function is responsible for retrieving the current value of the setting. It receives just one parameter, the root HTML element returned from the render function, which can be useful for getting the current value. | The <code>get</code> function is responsible for retrieving the current value of the setting. It receives just one parameter, the root HTML element returned from the render function, which can be useful for getting the current value. | ||
<nowiki>// get function for simple text input defined by above render | <nowiki>// get function for simple text input defined by above render | ||
function get(root) { | function get(root) { | ||
Line 336: | Line 385: | ||
}</nowiki> | }</nowiki> | ||
===== | ===== set(root: HTMLElement, value: any): void ===== | ||
The <code>set</code> function is responsible to keeping the HTML up-to-date with the current value (if updated programmatically). It receives the root HTML element from the render function and the value being set as the two parameters. | The <code>set</code> function is responsible to keeping the HTML up-to-date with the current value (if updated programmatically). It receives the root HTML element from the render function and the value being set as the two parameters. | ||
<nowiki>// set function for simple text setting defined above | <nowiki>// set function for simple text setting defined above | ||
function set(root, value) { | function set(root, value) { | ||
Line 344: | Line 395: | ||
==== Example ==== | ==== Example ==== | ||
<nowiki>// Use functions defined in above examples as reference | <nowiki>// Use functions defined in above examples as reference | ||
ctx.settings.type('simpleText', { | ctx.settings.type('simpleText', { | ||
Line 352: | Line 404: | ||
=== Built-In Types === | === Built-In Types === | ||
==== Base Setting Configuration ==== | ==== Base Setting Configuration ==== | ||
All individual settings inherit this base setting config object. | All individual settings inherit this base setting config object. | ||
<nowiki>interface SettingConfig { | <nowiki>interface SettingConfig { | ||
type: string; // Type of the setting | type: string; // Type of the setting | ||
Line 370: | Line 425: | ||
==== Text ==== | ==== Text ==== | ||
A simple textbox that accepts any character by default. Value is of type <code>string</code>. | A simple textbox that accepts any character by default. Value is of type <code>string</code>. | ||
<nowiki>interface TextConfig implements SettingConfig { | <nowiki>interface TextConfig implements SettingConfig { | ||
type: 'text'; | type: 'text'; | ||
Line 377: | Line 434: | ||
==== Number ==== | ==== Number ==== | ||
A simple textbox that only accepts numbers. Value is of type <code>number</code>. | A simple textbox that only accepts numbers. Value is of type <code>number</code>. | ||
<nowiki>interface NumberConfig implements SettingConfig { | <nowiki>interface NumberConfig implements SettingConfig { | ||
type: 'number'; | type: 'number'; | ||
Line 385: | Line 444: | ||
==== Switch ==== | ==== Switch ==== | ||
An on/off toggle switch. Value is of type <code>boolean</code>. | An on/off toggle switch. Value is of type <code>boolean</code>. | ||
<nowiki>interface SwitchConfig implements SettingConfig { | <nowiki>interface SwitchConfig implements SettingConfig { | ||
type: 'switch' | type: 'switch' | ||
Line 391: | Line 452: | ||
==== Dropdown ==== | ==== Dropdown ==== | ||
A dropdown button. Example: "Default Page on Load" game setting. Value is of type <code>any</code>. | A dropdown button. Example: "Default Page on Load" game setting. Value is of type <code>any</code>. | ||
<nowiki>DropdownConfig implements SettingConfig { | <nowiki>DropdownConfig implements SettingConfig { | ||
type: 'dropdown'; | type: 'dropdown'; | ||
Line 399: | Line 462: | ||
The <code>options</code> option defines the dropdown options available to be selected. Dropdown option schema is: | The <code>options</code> option defines the dropdown options available to be selected. Dropdown option schema is: | ||
<nowiki>interface DropdownOption { | <nowiki>interface DropdownOption { | ||
value: any; // value that is used by the setting | value: any; // value that is used by the setting | ||
Line 405: | Line 469: | ||
==== Button ==== | ==== Button ==== | ||
A button. Value is <code>undefined</code>. | A button. Value is <code>undefined</code>. | ||
<nowiki>interface ButtonConfig implements SettingConfig { | <nowiki>interface ButtonConfig implements SettingConfig { | ||
type: 'button'; | type: 'button'; |
Revision as of 03:24, 16 October 2022
Accessing the Mod Context Object
All examples in this guide will assume a mod context object ctx is in the current scope.
From a Module
If the module is defined as the "setup"
for your mod in manifest.json
, the exported setup
function will receive the context object as a sole parameter:
export function setup(ctx) { // ... }
Otherwise, use the global mod.getContext
method, passing in your module's meta object:
const ctx = mod.getContext(import.meta);
From a Script
The recommended approach for scripts is to use the global mod.register
method. This only works in scripts injected via the "load"
property of manifest.json
or the loadScript
method of the context object.
mod.register(ctx => { // ... });
From a Lifecycle Method
All game lifecycle method callbacks will also receive their respective mod's context object as a sole parameter.
onCharacterLoaded(ctx => { // ... });
From the Dev Context
For easier prototyping, you can use the global mod.getDevContext
method to get a special dev mod context object. This should not be used in a mod, but only for development purposes (via the console). Any APIs that require resources will not work as the dev "mod" does not contain any resources.
const devCtx = mod.getDevContext();
Loading Resources
🚨 All resource file paths must be relative to the root of your mod 🚨
getResourceUrl(path: string): string
Retrieves a usable URL for any resource packaged in your mod.
Parameters
path: string
The relative path to the resource to generate a URL for
Returns
string
The URL to the requested resource
Example
const url = ctx.getResourceUrl('sea-shanty-2.ogg'); const song = new Audio(url); song.loop = true; song.play();
loadModule(path: string): Promise<any>
Dynamically imports a JavaScript module.
Parameters
path: string
The relative path to the module resource
Returns
Promise<any>
A promise that resolves to an object containing all exported features from the module
Example
// my-module.mjs export function greet(name) { console.log(`Hello, ${name}!`); }
const myModule = await ctx.loadModule('my-module.mjs'); myModule.greet('Melvor'); // Hello, Melvor!
loadScript(path: string): Promise<void>
Injects a JavaScript file into the page.
Parameters
path: string
The relative path to the script resource
Returns
Promise<void>
A promise that resolves when the injected script has finished running
Example
// Await call if wanting the script to run before continuing await ctx.loadScript('my-script.js'); // my-script.js has run // Don't await if no dependency on script ctx.loadScript('my-independednt-script.js'); // my-independent-script.js has NOT run yet
loadTemplates(path: string): void
Inject all <template> elements contained in a given HTML file into the document body.
Parameters
path: string
The relative path to the HTML resource
Example
ctx.loadTemplates('my-templates.html');
loadStylesheet(path: string): void
Injects a CSS stylesheet into the page.
Parameters
path: string
The relative path to the stylesheet resource
Example
ctx.loadStylesheet('my-styles.css');
loadData(path: string): Promise<any>
Loads data from a JSON resource.
Parameters
path: string
The relative path to the JSON resource
Returns
Promise<any>
A promise that resolves to the parsed JSON object
Example
// my-data.json { "coolThings": [ "rocks" ] }
// in JavaScript const myData = await ctx.loadData('my-data.json'); console.log(myData.coolThings); // ['rocks']
Lifecycle Hooks
onModsLoaded(callback: (ctx: ModContext) => void | Promise<void>): void
Execute code after all mods have been loaded (character select screen).
Parameters
callback: (ctx: ModContext) => void | Promise<void>
A callback function that receives the mod's context object as a parameter. Can be synchronous or asynchronous.
Example
ctx.onModsLoaded(async (ctx) => { // ... });
onCharacterSelectionLoaded(callback: (ctx: ModContext) => void | Promise<void>): void
Execute code after the the character selection screen has fully loaded.
Parameters
callback: (ctx: ModContext) => void | Promise<void>
A callback function that receives the mod's context object as a parameter. Can be synchronous or asynchronous.
Example
ctx.onCharacterSelectionLoaded(async (ctx) => { // ... });
onCharacterLoaded(callback: (ctx: ModContext) => void | Promise<void>): void
Execute code after the player's chosen character has loaded and all game objects are created, but before offline progress calculations.
Parameters
callback: (ctx: ModContext) => void | Promise<void>
A callback function that receives the mod's context object as a parameter. Can be synchronous or asynchronous.
Example
ctx.onCharacterLoaded(async (ctx) => { // ... });
onInterfaceReady(callback: (ctx: ModContext) => void | Promise<void>): void
Execute code after offline progress has been calculated and all in-game user interface elements have been created.
Parameters
callback: (ctx: ModContext) => void | Promise<void>
A callback function that receives the mod's context object as a parameter. Can be synchronous or asynchronous.
Example
ctx.onInterfaceReady(async (ctx) => { // ... });
Adding and Modifying Game Data
✔️ Not documented yet, but it is available ✔️
Mod Settings
The mod settings API can be accessed through the settings
property on the root context object.
section(name: string): Section
Gets or creates a settings section. The order that sections are created are the order they will display in a mod's settings window.
Parameters
name: string
The name of the section. This will be displayed as a header of the section in the settings window.
Returns
Section
The section's object, used to perform add, set, or get settings.
Example
ctx.settings.section('General'); ctx.settings.section('Other'); // Sections will be displayed in the settings window in this order // 1. General // 2. Other
Section.add(config: SettingConfig | SettingConfig[]): void
Adds a setting to the section. The order that settings are added to a section are the order they will display in a mod's settings window.
Parameters
config: SettingConfig | SettingConfig[]
The setting's configuration object or an array of configuration objects to add multiple settings at once. See Settings Types section below for setting configuration options.
Example
ctx.settings.section('General').add({ type: 'switch', name: 'awesomeness-detection', label: 'Awesomeness Detection', hint: 'Determines if you are awesome or not.', default: false }); ctx.settings.section('Other').add([{ type: 'label', label: 'I am just a label though my story seldom told...' }, { type: 'number', name: 'pick-a-number', label: 'Pick a Number', hint: '1 through 10' }]);
Section.get(name: string): any
Gets the current value of a setting by its name property.
Parameters
name: string
The name of the setting to get the value of
Returns
any
The current value of the setting
Example
// Assuming the player has typed "1" into the setting ctx.settings.section('Other').get('pick-a-number'); // 1
Section.set(name: string, value: any): void
Programmatically sets the value of a setting by its name property.
Parameters
name: string
The name of the setting to set the value of
value: any
The value to set the setting to
Example
ctx.settings.section('Other').set('pick-a-number', 5);
type(name: string, config: SettingTypeConfig): void
Registers a setting type that can then be used by by any mod when adding a setting.
Parameters
name: string
The name of the setting type. This is what should be used for the type property of a setting configuration when adding a new setting. Other mods have to prepend the name with your mod's namespace.
config: SettingTypeConfig
An object defining the setting type's behavior. See definition below.
Example
// manifest.json { "namespace": "my_mod", // ... }
ctx.settings.type('customText', { // See example config in SettingTypeConfig section below }); ctx.settings.section('Genera').add({ type: 'customText', // ... });
Other mods will have to add your namespace to use your custom type:
ctx.settings.section('Other').add({ type: 'my_mod:customText', // ... });
SettingTypeConfig
All functions are required.
render(name: string, onChange: () => void, config: SettingConfig): HTMLElement
The render function is responsible for using any properties passed into the config to render HTML for the setting.
The name
parameter should be used as a form of id in the setting's HTML, if needed. The common use case for this is setting an <input>
's name
and id
attributes to this value, and then setting a <label>
's for
attribute to this value as well.
The onChange
parameter should be called when this setting's value is changed. The common use case for this is adding this as an event listener to an <input>
element's change
event.
The config
parameter holds all values passed in the config object when this setting is being added. For example, the label
, hint
, default
, etc. properties.
Individual settings can opt to return validation errors in their onChange
method. You can give a place to display this validation error in an element with a class of validation-message
.
// The render function for a simple text box function render(name, onChange, config) { const input = document.createElement('input'); input.id = name; input.type = 'text'; input.name = name; const label = document.createElement('label'); label.for = name; label.textContent = config.label; if (config.hint) { const hint = document.createElement('small'); hint.textContent = config.hint; label.appendChile(hint); } const validation = document.createElement('small'); validation.classList.add('text-danger', 'validation-message'); const root = document.createElement('div'); root.appendChild(input); root.appendChild(label); root.appendChild(validation); return root; }
get(root: HTMLElement): any
The get
function is responsible for retrieving the current value of the setting. It receives just one parameter, the root HTML element returned from the render function, which can be useful for getting the current value.
// get function for simple text input defined by above render function get(root) { return root.querySelector('input').value; }
set(root: HTMLElement, value: any): void
The set
function is responsible to keeping the HTML up-to-date with the current value (if updated programmatically). It receives the root HTML element from the render function and the value being set as the two parameters.
// set function for simple text setting defined above function set(root, value) { root.querySelector('input').value = value; }
Example
// Use functions defined in above examples as reference ctx.settings.type('simpleText', { render: render, get: get, set: set });
Built-In Types
Base Setting Configuration
All individual settings inherit this base setting config object.
interface SettingConfig { type: string; // Type of the setting name: string; // Name of the setting label: string | HTMLElement; // Display label for the setting hint: string | HTMLElement; // Small help text to display alongside the setting default: any; // Default value for the setting onChange(value: any, previousValue: any): void | boolean | string // See notes }
The onChange
option is a callback function that receives the new value being set and the previous value of the setting. This function can optionally return a value to serve as a validator:
- No return value /
undefined
/true
/ truth-y (non-string) value: Validates successfully and allows the value to be changed false
/ false-y value: Validation fails and setting value is restored to previousstring
value: Validation fails, setting value is restored to previous, and the string contents are displayed in a.validation-message
element, if available (see custom render above)
Text
A simple textbox that accepts any character by default. Value is of type string
.
interface TextConfig implements SettingConfig { type: 'text'; maxLength: number; // Max length attribute for the textbox }
Number
A simple textbox that only accepts numbers. Value is of type number
.
interface NumberConfig implements SettingConfig { type: 'number'; min: number; // Minimum value to be entered max: number; // Maximum value to be entered }
Switch
An on/off toggle switch. Value is of type boolean
.
interface SwitchConfig implements SettingConfig { type: 'switch' }
Dropdown
A dropdown button. Example: "Default Page on Load" game setting. Value is of type any
.
DropdownConfig implements SettingConfig { type: 'dropdown'; color: string; // see Button config options: DropdownOption[]; // see note }
The options
option defines the dropdown options available to be selected. Dropdown option schema is:
interface DropdownOption { value: any; // value that is used by the setting display: string | HTMLElement; // display text or element on the option }
Button
A button. Value is undefined
.
interface ButtonConfig implements SettingConfig { type: 'button'; display: string | HTMLElement; // displayed text or element inside the button color: string; // see note onClick(): void; // triggered on click of the button }
The color
option is appended to a CSS class starting with btn-
and defaults to primary
(btn-primary
) if not defined. Default colors available:
- primary: blue
- secondary: grey
- success: green
- info: light blue
- warning: yellow
- danger: red
- dark: dark grey
Checkbox Group
A group of checkboxes. Value is of type any[]
.
interface CheckboxGroupConfig implements SettingConfig { type: 'checkbox-group'; options: CheckboxOption[]; // see note }
The options
option defines the checkboxes that are available to be selected. Checkbox option schema is:
interface CheckboxOption { value: any; // value to be added to array that is set as setting value label: string | HTMLElement; hint: string | HTMLElement; }
Radio Group
A group of radio buttons. Value is of type any
.
interface RadioGroupConfig implements SettingConfig { type: 'radio-group'; options: CheckboxOption[]; // see checkbox group's options schema }
Label
A simple label. Value is undefined
.
interface LabelConfig implements SettingConfig { type: 'label'; }
Custom
A custom-rendered setting. See SettingTypeConfig section above. This is different from registering a custom setting type as this is a one-off and will not register the type for reuse. Value is of type any
.
interface CustomConfig implements SettingConfig, SettingTypeConfig { type: 'custom'; }
Character Data Storage
The character storage API can be accessed through the characterStorage
property on the root context object.
Limitations
The character storage can only be used once a character has been loaded (after lifecycle hook onCharacterLoaded
).
Each character can store up to 8,192 bytes (8kb) of data per mod, including keys. Only JSON-serializable data can be stored. This includes primitive types (string
, number
, boolean
) and objects and arrays that contain only primitive types or other objects or arrays that fit this description. This serialization/deserialization is handled automatically.
setItem(key: string, data: any): void
Sets a key/value pair in character storage.
Parameters
key: string
The key to identify the data being stored. Used in calls to getItem
and removeItem
.
data: any
The data to be stored. See limitations above.
Example
ctx.characterStorage.setItem('coolThings', ['rocks']);
getItem(key: string): any
Gets a value by its key from character storage.
Parameters
key: string The key of the data to retrieve
Returns
any
The data retrieved. Returns undefined
if no such key is stored.
Example
ctx.characterStorage.getItem('coolThings'); // returns ['rocks']
removeItem(key: string): void
Removes a key/value pair by key from character storage.
Parameters
key: string
The key of the key/value pair to remove
Example
ctx.characterStorage.removeItem('coolThings'); ctx.characterStorage.getItem('coolThings'); // returns undefined
clear(): void
Removes all key/value pairs from character storage.
Example
ctx.characterStorage.clear();
Account Data Storage
The account storage API can be accessed through the accountStorage
property on the root context object.
Limitations
Due to the cloud-based nature of how account data is stored and potential network issues the player may experience, data integrity is not 100% guaranteed in the account storage. Account storage is advised to be used sparingly.
An account can store up to 8,192 bytes (8kb) of data per mod, including keys. Only JSON-serializable data can be stored. This includes primitive types (string
, number
, boolean
) and objects and arrays that contain only primitive types or other objects or arrays that fit this description. This serialization/deserialization is handled automatically.
setItem(key: string, data: any): void
Sets a key/value pair in account storage.
Parameters
key: string
The key to identify the data being stored. Used in calls to getItem
and removeItem
.
data: any
The data to be stored. See limitations above.
Example
ctx.accountStorage.setItem('coolThings', ['rocks']);
getItem(key: string): any
Gets a value by its key from account storage.
Parameters
key: string
The key of the data to retrieve
Returns
any
The data retrieved. Returns undefined
if no such key is stored.
Example
ctx.accountStorage.getItem('coolThings'); // returns ['rocks']
removeItem(key: string): void
Removes a key/value pair by key from account storage.
Parameters
key: string
The key of the key/value pair to remove
Example
ctx.accountStorage.removeItem('coolThings'); ctx.accountStorage.getItem('coolThings'); // returns undefined
clear(): void
Removes all key/value pairs from account storage.
Example
ctx.accountStorage.clear();
Game Method Patching/Hooking
patch(className: class, methodName: string): Patch
This is the entry-point to the method patching API. The Patch object returned by this method can perform further actions with the specified class and method.
Parameters
className: class
Class containing the method you want to patch. Should be the actual class reference, not a string, e.g. Skill
, not 'Skill'
.
methodName: string
Name of the method to patch.
Returns
Patch
A patch object for the specified class and method. See below for usage.
Example
ctx.patch(Skill, 'addXP');
Patch.before(hook: (...args: any) => any[] | void): void
Execute a callback function immediately before the method body is called. The callback function's parameters are the arguments being passed into the method call. Optionally the callback function can return an array of values to override the arguments being passed to the method body. If no return value is specified (returns undefined
), the arguments are left as-is.
Parameters
hook: (...args: any) => any[] | void
The callback hook to be executed.
Example
// Double all XP gains ctx.patch(Skill, 'addXP').before((amount, masteryAction) => [amount * 2, masteryAction]);
Patch.after(hook: (returnValue: any, ...args: any) => any | void): void
Execute a callback function immediate after the method body is finished executing. The callback function's first parameter is the value returned from the method body. The rest of the parameters are the arguments that were passed into the method body. Optionally the callback function can return a new value to override the method's return value. If no return value is specified (returns undefined
), the return value is left as-is.
Parameters
hook: (returnValue: any, ...args: any) => any | void
The callback hook to be executed.
Example
// The player never misses an attack ctx.patch(Player, 'rollToHit').after(willHit => { if (!willHit) console.log('A miss? I think not!'); return true; });
Patch.replace(replacement: (replacedMethod: (...args: any) => any, ...args: any) => any): void
Execute a callback function instead of the method's current body. The callback function's first parameter is the replaced method body. The rest of the parameters are the arguments that were to be passed to the method. The callback function's return value is the return value for the method. The replacement function is still subject to argument/return value modifications made in before
and after
hooks, respectively.
Parameters
replacement: (replacedMethod: (...args: any) => any, ...args: any) => any
The callback function to replace the method body.
Example
ctx.patch(Skill, 'addXP').replace(function(o, amount, masteryAction) { // Prevent any woodcutting XP if (this.id === 'melvorD:Woodcutting') return; // Double any mining XP if (this.id === 'melvorD:Mining') return o(amount * 2, masteryAction); // Grant all other XP as normal return o(amount, masteryAction); });
It's important to note that using the replace
method replaces the current method body, meaning multiple calls of the replace
method get executed in the reverse order that they were declared:
const xpPatch = ctx.patch(Skill, 'addXP'); xpPatch.replace((o, amount, masteryAction) => { console.log('Replacement #1'); return o(amount, masteryAction); }); xpPatch.replace({o, amount, masteryAction) => { console.log('Replacement #2'); return o(amount, masteryAction); }); game.woodcutting.addXP(100); // Logs: // Replacement #2 // Replacement #1
isPatched(className: class, methodName: string): boolean
Checks whether or not a method has been patched.
Parameters
className: class
Class containing the method to check for having been patched. Should be the actual class reference, not a string, e.g. Skill
, not 'Skill'
.
methodName: string
Name of the method to check.
Returns
boolean
Whether or not the given class method is patched.
Example
ctx.isPatched(Skill, 'addXP'); // false ctx.patch(Skill, 'addXP'); ctx.isPatched(Skill, 'addXP'); // true
Exposing Properties and Methods (Mod API)
You may want to allow other mods to be able to interact or integrate with your mod through an API you define. To do so, the recommended approach is through the api
method on the context object. After defining an API using the method below, other mods can access it through the global mod.api['your_mods_namespace']
object.
api(endpoints?: object): object
Specify properties and methods to expose on the global mod.api['your_mods_namespace']
object. Can be called multiple times to append more endpoints.
Parameters
endpoint: object
An object containing any properties or methods you want to expose. Can be omitted to just retrieve your mod's current API object.
Returns
object
The mod's API object
Example
// manifest.json { "namespace": "helloWorld", "setup": "setup.mjs" }
// setup.mjs export function setup({ api }) { api({ greet: name => console.log(`Hello, ${name!}`); }); }
Other mods would then be able to interact with your API:
// some other mod mod.api.helloWorld.greet('Melvor'); // Hello, Melvor!