Mod Creation/Mod Context API Reference: Difference between revisions

From Melvor Idle
No edit summary
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''' 🚨


=== <code>getResourceUrl(path: string): string</code> ===
=== 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>


=== <code>loadModule(path: string): Promise<any></code> ===
=== 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>


=== <code>loadScript(path: string): Promise<void></code> ===
=== 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>


=== <code>loadTemplates(path: string): void</code> ===
=== 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>


=== <code>loadStylesheet(path: string): void</code> ===
=== 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>


=== <code>loadData(path: string): Promise<any></code> ===
=== 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 ==
=== <code>onModsLoaded(callback: (ctx: ModContext) => void | Promise<void>): void</code> ===
 
=== 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>


=== <code>onCharacterSelectionLoaded(callback: (ctx: ModContext) => void | Promise<void>): void</code> ===
=== 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>


=== <code>onCharacterLoaded(callback: (ctx: ModContext) => void | Promise<void>): void</code> ===
=== 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>


=== <code>onInterfaceReady(callback: (ctx: ModContext) => void | Promise<void>): void</code> ===
=== 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.


=== <code>section(name: string): Section</code> ===
=== 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>


==== <code>Section.add(config: SettingConfig | SettingConfig[]): void</code> ====
==== 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>


==== <code>Section.get(name: string): any</code> ====
==== 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>


==== <code>Section.set(name: string, value: any): void</code> ====
==== 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>


=== <code>type(name: string, config: SettingTypeConfig): void</code> ===
=== 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>


==== <code>SettingTypeConfig</code> ====
==== SettingTypeConfig ====
 
All functions are required.
All functions are required.


===== <code>render(name: string, onChange: () => void, config: SettingConfig): HTMLElement</code> =====
===== 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>


===== <code>get(root: HTMLElement): any</code> =====
===== 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>


===== <code>set(root: HTMLElement, value: any): void</code> =====
===== 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';