2
edits
Buttchouda (talk | contribs) |
m (fix double "the") |
||
(27 intermediate revisions by 3 users not shown) | |||
Line 7: | Line 7: | ||
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: | ||
< | <syntaxhighlight lang="js" line>export function setup(ctx) { | ||
// ... | // ... | ||
}</ | }</syntaxhighlight> | ||
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: | ||
< | <syntaxhighlight lang="js" line>const ctx = mod.getContext(import.meta);</syntaxhighlight> | ||
=== From a Script === | === From a Script === | ||
Line 19: | Line 19: | ||
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. | ||
< | <syntaxhighlight lang="js" line>mod.register(ctx => { | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
=== From a Lifecycle Method === | === From a Lifecycle Method === | ||
Line 27: | Line 27: | ||
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. | ||
< | <syntaxhighlight lang="js" line>onCharacterLoaded(ctx => { | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
=== From the Dev Context === | === From the Dev Context === | ||
Line 35: | Line 35: | ||
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. | ||
< | <syntaxhighlight lang="js" line>const devCtx = mod.getDevContext();</syntaxhighlight> | ||
== Getter Properties == | |||
=== name: string === | |||
The name of the mod. | |||
=== namespace: string | undefined === | |||
The defined namespace of the mod, if provided. | |||
=== version: string === | |||
The currently loaded version of the mod. | |||
== Loading Resources == | == Loading Resources == | ||
Line 55: | Line 69: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>const url = ctx.getResourceUrl('sea-shanty-2.ogg'); | ||
const song = new Audio(url); | const song = new Audio(url); | ||
song.loop = true; | song.loop = true; | ||
song.play();</ | song.play();</syntaxhighlight> | ||
=== loadModule(path: string): Promise<any> === | === loadModule(path: string): Promise<any> === | ||
Line 74: | Line 88: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// my-module.mjs | ||
export function greet(name) { | export function greet(name) { | ||
console.log(`Hello, ${name}!`); | console.log(`Hello, ${name}!`); | ||
}</ | }</syntaxhighlight> | ||
< | <syntaxhighlight lang="js" line>const myModule = await ctx.loadModule('my-module.mjs'); | ||
myModule.greet('Melvor'); // Hello, Melvor!</ | myModule.greet('Melvor'); // Hello, Melvor!</syntaxhighlight> | ||
=== loadScript(path: string): Promise<void> === | === loadScript(path: string): Promise<void> === | ||
Line 96: | Line 110: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// Await call if wanting the script to run before continuing | ||
await ctx.loadScript('my-script.js'); | await ctx.loadScript('my-script.js'); | ||
// my-script.js has run | // my-script.js has run | ||
Line 102: | Line 116: | ||
// Don't await if no dependency on script | // Don't await if no dependency on script | ||
ctx.loadScript('my-independednt-script.js'); | ctx.loadScript('my-independednt-script.js'); | ||
// my-independent-script.js has NOT run yet</ | // my-independent-script.js has NOT run yet</syntaxhighlight> | ||
=== loadTemplates(path: string): void === | === loadTemplates(path: string): Promise<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 111: | Line 125: | ||
<code>path: string</code> The relative path to the HTML resource | <code>path: string</code> The relative path to the HTML resource | ||
'''Returns''' | |||
<code>Promise<void></code> A promise that is resolved once all templates have been injected into the document body. | |||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.loadTemplates('my-templates.html');</syntaxhighlight> | ||
=== loadStylesheet(path: string): void === | === loadStylesheet(path: string): void === | ||
Line 126: | Line 144: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.loadStylesheet('my-styles.css');</syntaxhighlight> | ||
=== loadData(path: string): Promise<any> === | === loadData(path: string): Promise<any> === | ||
Line 142: | Line 160: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// my-data.json | ||
{ | { | ||
"coolThings": [ | "coolThings": [ | ||
"rocks" | "rocks" | ||
] | ] | ||
}</ | }</syntaxhighlight> | ||
< | <small>''Comments in JSON are purely illustrative and not valid markup''</small> | ||
<syntaxhighlight lang="js" line>// in JavaScript | |||
const myData = await ctx.loadData('my-data.json'); | const myData = await ctx.loadData('my-data.json'); | ||
console.log(myData.coolThings); // ['rocks']</ | console.log(myData.coolThings); // ['rocks']</syntaxhighlight> | ||
== Sharing Resources == | |||
=== share(resourcePath: string): void === | |||
Shares a packed mod resource for other mods to use. | |||
'''Parameters''' | |||
<code>resourcePath: string</code> The resource path to be shared. | |||
'''Example''' | |||
<syntaxhighlight lang="js" line>// manifest.json | |||
{ | |||
"namespace": "helloMelvor" | |||
}</syntaxhighlight> | |||
<small>''Comments in JSON are purely illustrative and not valid markup''</small> | |||
<syntaxhighlight lang="js" line>// in JavaScript | |||
ctx.share('my_cool_image.png'); | |||
ctx.share('Greeter.mjs');</syntaxhighlight> | |||
Then another mod can use the resource anywhere that accepts a mod resource path. | |||
<syntaxhighlight lang="js" line> | |||
ctx.getResourceUrl('helloMelvor:my_cool_image.png'); | |||
const { Greeter } = await loadModule('helloMelvor:Greeter.mjs'); | |||
const greeter = new Greeter();</syntaxhighlight> | |||
== Lifecycle Hooks == | == Lifecycle Hooks == | ||
Line 165: | Line 215: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.onModsLoaded(async (ctx) => { | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
=== onCharacterSelectionLoaded(callback: (ctx: ModContext) => void | Promise<void>): void === | === onCharacterSelectionLoaded(callback: (ctx: ModContext) => void | Promise<void>): void === | ||
Execute code after | Execute code after the character selection screen has fully loaded. | ||
'''Parameters''' | |||
<code>callback: (ctx: ModContext) => void | Promise<void></code> A callback function that receives the mod's context object as a parameter. Can be synchronous or asynchronous. | |||
'''Example''' | |||
<syntaxhighlight lang="js" line>ctx.onCharacterSelectionLoaded(async (ctx) => { | |||
// ... | |||
});</syntaxhighlight> | |||
=== onInterfaceAvailable(callback: (ctx: ModContext) => void | Promise<void>): void === | |||
Execute code before the character is loaded but after the game interface is initially injected into the page (but not initialized). Mostly useful for adding interface elements for custom skills that need to be present before <code>onCharacterLoaded</code>. | |||
'''Parameters''' | '''Parameters''' | ||
Line 179: | Line 243: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.onInterfaceAvailable(async (ctx) => { | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
=== onCharacterLoaded(callback: (ctx: ModContext) => void | Promise<void>): void === | === onCharacterLoaded(callback: (ctx: ModContext) => void | Promise<void>): void === | ||
Line 193: | Line 257: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.onCharacterLoaded(async (ctx) => { | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
=== onInterfaceReady(callback: (ctx: ModContext) => void | Promise<void>): void === | === onInterfaceReady(callback: (ctx: ModContext) => void | Promise<void>): void === | ||
Line 207: | Line 271: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.onInterfaceReady(async (ctx) => { | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
== Game Object Registration == | |||
The game object registration API can be accessed through the <code>gameData</code> property on the root context object. | |||
=== addPackage(data: string | GameDataPackage): Promise<void> === | |||
Registers a game data package. | |||
'''Parameters''' | |||
<code>data: string | GameDataPackage</code> The resource path to your game data package <code>.json</code> file or a valid JavaScript GameDataPackage object. | |||
'''Example''' | |||
<syntaxhighlight lang="js" line>// data.json | |||
{ | |||
"$schema": "https://melvoridle.com/assets/schema/gameData.json", | |||
"data": { | |||
// data objects here | |||
} | |||
}</syntaxhighlight> | |||
<small>''Comments in JSON are purely illustrative and not valid markup''</small> | |||
<syntaxhighlight lang="js" line>await ctx.gameData.addPackage('data.json');</syntaxhighlight> | |||
=== buildPackage(builder: (packageBuilder: GameDataPackageBuilder) => void): BuiltGameDataPackage === | |||
Builds a GameDataPackage object using the <code>GameDataPackageBuilder</code> API. | |||
'''Parameters''' | |||
<code>builder: (packageBuilder: GameDataPackageBuilder) => void</code> The builder to be used to add individual game objects to the data package. | |||
'''Returns''' | |||
<code>BuiltGameDataPackage</code> A wrapper for the game data package. See information below. | |||
'''Example''' | |||
<syntaxhighlight lang="js" line>ctx.gameData.buildPackage((p) => { | |||
// data registration here | |||
});</syntaxhighlight> | |||
==== BuiltGameDataPackage.package: GameDataPackage ==== | |||
(Property) The actual built <code>GameDataPackage</code> object. | |||
==== BuiltGameDataPackage.add(): void ==== | |||
Registers the built game data package. | |||
'''Example''' | |||
<syntaxhighlight lang="js" line>const pkg = ctx.gameData.buildPackage((p) => { /* ... */ }); | |||
pkg.add();</syntaxhighlight> | |||
== Mod Settings == | == Mod Settings == | ||
{{Disclaimer|When loading your mod as a Local Mod via the Creator Toolkit, the mod must be linked to mod.io and you must have subscribed to and installed the mod via mod.io in order for this data to persist.}} | |||
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. | ||
Line 233: | Line 352: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.settings.section('General'); | ||
ctx.settings.section('Other'); | ctx.settings.section('Other'); | ||
// Sections will be displayed in the settings window in this order | // Sections will be displayed in the settings window in this order | ||
// 1. General | // 1. General | ||
// 2. Other</ | // 2. Other</syntaxhighlight> | ||
==== Section.add(config: SettingConfig | SettingConfig[]): void ==== | ==== Section.add(config: SettingConfig | SettingConfig[]): void ==== | ||
Line 249: | Line 368: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.settings.section('General').add({ | ||
type: 'switch', | type: 'switch', | ||
name: 'awesomeness-detection', | name: 'awesomeness-detection', | ||
Line 265: | Line 384: | ||
label: 'Pick a Number', | label: 'Pick a Number', | ||
hint: '1 through 10' | hint: '1 through 10' | ||
}]);</ | }]);</syntaxhighlight> | ||
==== Section.get(name: string): any ==== | ==== Section.get(name: string): any ==== | ||
Line 281: | Line 400: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// Assuming the player has typed "1" into the setting | ||
ctx.settings.section('Other').get('pick-a-number'); // 1</ | ctx.settings.section('Other').get('pick-a-number'); // 1</syntaxhighlight> | ||
==== Section.set(name: string, value: any): void ==== | ==== Section.set(name: string, value: any): void ==== | ||
Line 296: | Line 415: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.settings.section('Other').set('pick-a-number', 5);</syntaxhighlight> | ||
=== type(name: string, config: SettingTypeConfig): void === | === type(name: string, config: SettingTypeConfig): void === | ||
Line 310: | Line 429: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// manifest.json | ||
{ | { | ||
"namespace": "my_mod", | "namespace": "my_mod", | ||
// ... | // ... | ||
}</ | }</syntaxhighlight> | ||
<small>''Comments in JSON are purely illustrative and not valid markup''</small> | |||
< | <syntaxhighlight lang="js" line>ctx.settings.type('customText', { | ||
// See example config in SettingTypeConfig section below | // See example config in SettingTypeConfig section below | ||
}); | }); | ||
ctx.settings.section(' | ctx.settings.section('General').add({ | ||
type: 'customText', | type: 'customText', | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
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: | ||
< | <syntaxhighlight lang="js" line>ctx.settings.section('Other').add({ | ||
type: 'my_mod:customText', | type: 'my_mod:customText', | ||
// ... | // ... | ||
});</ | });</syntaxhighlight> | ||
==== SettingTypeConfig ==== | ==== SettingTypeConfig ==== | ||
Line 348: | Line 469: | ||
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>. | ||
< | <syntaxhighlight lang="js" line>// The render function for a simple text box | ||
function render(name, onChange, config) { | function render(name, onChange, config) { | ||
const input = document.createElement('input'); | const input = document.createElement('input'); | ||
Line 354: | Line 475: | ||
input.type = 'text'; | input.type = 'text'; | ||
input.name = name; | input.name = name; | ||
input.addEventListener('change', () => onChange()); | |||
const label = document.createElement('label'); | const label = document.createElement('label'); | ||
Line 362: | Line 484: | ||
const hint = document.createElement('small'); | const hint = document.createElement('small'); | ||
hint.textContent = config.hint; | hint.textContent = config.hint; | ||
label. | label.appendChild(hint); | ||
} | } | ||
Line 374: | Line 496: | ||
return root; | return root; | ||
}</ | }</syntaxhighlight> | ||
===== get(root: HTMLElement): any ===== | ===== get(root: HTMLElement): any ===== | ||
Line 380: | Line 502: | ||
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. | ||
< | <syntaxhighlight lang="js" line>// get function for simple text input defined by above render | ||
function get(root) { | function get(root) { | ||
return root.querySelector('input').value; | return root.querySelector('input').value; | ||
}</ | }</syntaxhighlight> | ||
===== set(root: HTMLElement, value: any): void ===== | ===== set(root: HTMLElement, value: any): void ===== | ||
Line 389: | Line 511: | ||
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. | ||
< | <syntaxhighlight lang="js" line>// set function for simple text setting defined above | ||
function set(root, value) { | function set(root, value) { | ||
root.querySelector('input').value = value; | root.querySelector('input').value = value; | ||
}</ | }</syntaxhighlight> | ||
==== Example ==== | ==== Example ==== | ||
< | <syntaxhighlight lang="js" line>// Use functions defined in above examples as reference | ||
ctx.settings.type('simpleText', { | ctx.settings.type('simpleText', { | ||
render: render, | render: render, | ||
get: get, | get: get, | ||
set: set | set: set | ||
});</ | });</syntaxhighlight> | ||
=== Built-In Types === | === Built-In Types === | ||
Line 409: | Line 531: | ||
All individual settings inherit this base setting config object. | All individual settings inherit this base setting config object. | ||
< | <syntaxhighlight lang="js" line>interface SettingConfig { | ||
type: string; // Type of the setting | type: string; // Type of the setting | ||
name: string; // Name of the setting | name: string; // Name of the setting | ||
Line 416: | Line 538: | ||
default: any; // Default value for the setting | default: any; // Default value for the setting | ||
onChange(value: any, previousValue: any): void | boolean | string // See notes | onChange(value: any, previousValue: any): void | boolean | string // See notes | ||
}</ | }</syntaxhighlight> | ||
The <code>onChange</code> 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: | The <code>onChange</code> 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: | ||
Line 428: | Line 550: | ||
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>. | ||
< | <syntaxhighlight lang="js" line>interface TextConfig implements SettingConfig { | ||
type: 'text'; | type: 'text'; | ||
maxLength: number; // Max length attribute for the textbox | maxLength: number; // Max length attribute for the textbox | ||
}</ | }</syntaxhighlight> | ||
==== Number ==== | ==== Number ==== | ||
Line 437: | Line 559: | ||
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>. | ||
< | <syntaxhighlight lang="js" line>interface NumberConfig implements SettingConfig { | ||
type: 'number'; | type: 'number'; | ||
min: number; // Minimum value to be entered | min: number; // Minimum value to be entered | ||
max: number; // Maximum value to be entered | max: number; // Maximum value to be entered | ||
}</ | }</syntaxhighlight> | ||
==== Switch ==== | ==== Switch ==== | ||
Line 447: | Line 569: | ||
An on/off toggle switch. Value is of type <code>boolean</code>. | An on/off toggle switch. Value is of type <code>boolean</code>. | ||
< | <syntaxhighlight lang="js" line>interface SwitchConfig implements SettingConfig { | ||
type: 'switch' | type: 'switch' | ||
}</ | }</syntaxhighlight> | ||
==== Dropdown ==== | ==== Dropdown ==== | ||
Line 455: | Line 577: | ||
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>. | ||
< | <syntaxhighlight lang="js" line>DropdownConfig implements SettingConfig { | ||
type: 'dropdown'; | type: 'dropdown'; | ||
color: string; // see Button config | color: string; // see Button config | ||
options: DropdownOption[]; // see note | options: DropdownOption[]; // see note | ||
}</ | }</syntaxhighlight> | ||
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: | ||
< | <syntaxhighlight lang="js" line>interface DropdownOption { | ||
value: any; // value that is used by the setting | value: any; // value that is used by the setting | ||
display: string | HTMLElement; // display text or element on the option | display: string | HTMLElement; // display text or element on the option | ||
}</ | }</syntaxhighlight> | ||
==== Button ==== | ==== Button ==== | ||
Line 472: | Line 594: | ||
A button. Value is <code>undefined</code>. | A button. Value is <code>undefined</code>. | ||
< | <syntaxhighlight lang="js" line>interface ButtonConfig implements SettingConfig { | ||
type: 'button'; | type: 'button'; | ||
display: string | HTMLElement; // displayed text or element inside the button | display: string | HTMLElement; // displayed text or element inside the button | ||
color: string; // see note | color: string; // see note | ||
onClick(): void; // triggered on click of the button | onClick(): void; // triggered on click of the button | ||
}</ | }</syntaxhighlight> | ||
The <code>color</code> option is appended to a CSS class starting with <code>btn-</code> and defaults to <code>primary</code> (<code>btn-primary</code>) if not defined. Default colors available: | The <code>color</code> option is appended to a CSS class starting with <code>btn-</code> and defaults to <code>primary</code> (<code>btn-primary</code>) if not defined. Default colors available: | ||
Line 491: | Line 613: | ||
==== Checkbox Group ==== | ==== Checkbox Group ==== | ||
A group of checkboxes. Value is of type <code>any[]</code>. | A group of checkboxes. Value is of type <code>any[]</code>. | ||
< | <syntaxhighlight lang="js" line>interface CheckboxGroupConfig implements SettingConfig { | ||
type: 'checkbox-group'; | type: 'checkbox-group'; | ||
options: CheckboxOption[]; // see note | options: CheckboxOption[]; // see note | ||
}</ | }</syntaxhighlight> | ||
The <code>options</code> option defines the checkboxes that are available to be selected. Checkbox option schema is: | The <code>options</code> option defines the checkboxes that are available to be selected. Checkbox option schema is: | ||
<syntaxhighlight lang="js" line>interface CheckboxOption { | |||
value: any; // value to be added to array that is set as setting value | value: any; // value to be added to array that is set as setting value | ||
label: string | HTMLElement; | label: string | HTMLElement; | ||
hint: string | HTMLElement; | hint: string | HTMLElement; | ||
}</ | }</syntaxhighlight> | ||
==== Radio Group ==== | ==== Radio Group ==== | ||
A group of radio buttons. Value is of type <code>any</code>. | A group of radio buttons. Value is of type <code>any</code>. | ||
< | <syntaxhighlight lang="js" line>interface RadioGroupConfig implements SettingConfig { | ||
type: 'radio-group'; | type: 'radio-group'; | ||
options: CheckboxOption[]; // see checkbox group's options schema | options: CheckboxOption[]; // see checkbox group's options schema | ||
}</ | }</syntaxhighlight> | ||
==== Label ==== | ==== Label ==== | ||
A simple label. Value is <code>undefined</code>. | A simple label. Value is <code>undefined</code>. | ||
< | <syntaxhighlight lang="js" line>interface LabelConfig implements SettingConfig { | ||
type: 'label'; | type: 'label'; | ||
}</ | }</syntaxhighlight> | ||
==== Custom ==== | ==== Custom ==== | ||
A custom-rendered setting. See [[#SettingTypeConfig|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 <code>any</code>. | A custom-rendered setting. See [[#SettingTypeConfig|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 <code>any</code>. | ||
< | <syntaxhighlight lang="js" line>interface CustomConfig implements SettingConfig, SettingTypeConfig { | ||
type: 'custom'; | type: 'custom'; | ||
}</ | }</syntaxhighlight> | ||
== Character Data Storage == | == Character Data Storage == | ||
{{Disclaimer|When loading your mod as a Local Mod via the Creator Toolkit, the mod must be linked to mod.io and you must have subscribed to and installed the mod via mod.io in order for this data to persist.}} | |||
The character storage API can be accessed through the <code>characterStorage</code> property on the root context object. | The character storage API can be accessed through the <code>characterStorage</code> property on the root context object. | ||
Line 545: | Line 669: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.characterStorage.setItem('coolThings', ['rocks']);</syntaxhighlight> | ||
=== getItem(key: string): any === | === getItem(key: string): any === | ||
Line 561: | Line 685: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.characterStorage.getItem('coolThings'); // returns ['rocks']</syntaxhighlight> | ||
=== removeItem(key: string): void === | === removeItem(key: string): void === | ||
Line 573: | Line 697: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.characterStorage.removeItem('coolThings'); | ||
ctx.characterStorage.getItem('coolThings'); // returns undefined</ | ctx.characterStorage.getItem('coolThings'); // returns undefined</syntaxhighlight> | ||
=== clear(): void === | === clear(): void === | ||
Line 582: | Line 706: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.characterStorage.clear();</syntaxhighlight> | ||
== Account Data Storage == | == Account Data Storage == | ||
{{Disclaimer|When loading your mod as a Local Mod via the Creator Toolkit, the mod must be linked to mod.io and you must have subscribed to and installed the mod via mod.io in order for this data to persist.}} | |||
The account storage API can be accessed through the <code>accountStorage</code> property on the root context object. | The account storage API can be accessed through the <code>accountStorage</code> property on the root context object. | ||
Line 606: | Line 732: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.accountStorage.setItem('coolThings', ['rocks']);</syntaxhighlight> | ||
=== getItem(key: string): any === | === getItem(key: string): any === | ||
Line 622: | Line 748: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.accountStorage.getItem('coolThings'); // returns ['rocks']</syntaxhighlight> | ||
=== removeItem(key: string): void === | === removeItem(key: string): void === | ||
Line 634: | Line 760: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.accountStorage.removeItem('coolThings'); | ||
ctx.accountStorage.getItem('coolThings'); // returns undefined</ | ctx.accountStorage.getItem('coolThings'); // returns undefined</syntaxhighlight> | ||
=== clear(): void === | === clear(): void === | ||
Line 643: | Line 769: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.accountStorage.clear();</syntaxhighlight> | ||
== Game Object Patching/Hooking == | == Game Object Patching/Hooking == | ||
=== patch(className: class, | === patch(className: class, methodOrPropertyName: string): MethodPatch | PropertyPatch === | ||
This is the entry-point to the method patching API. The | This is the entry-point to the method and getter/setter patching API. Depending on if the second parameter is a method or getter/setter property, a <code>MethodPatch</code> or <code>PropertyPatch</code> object will be returned, respectively. The MethodPatch/PropertyPatch object should then be used to perform further actions with the specified class and method/property. | ||
'''Parameters''' | '''Parameters''' | ||
<code>className: class</code> Class containing the method you want to patch. Should be the actual class reference, not a string, e.g. <code>Skill</code>, not <code>'Skill'</code>. | <code>className: class</code> Class containing the method or getter/setter you want to patch. Should be the actual class reference, not a string, e.g. <code>Skill</code>, not <code>'Skill'</code>. | ||
<code> | <code>methodOrPropertyName: string</code> Name of the method or getter/setter property to patch. | ||
'''Returns''' | '''Returns''' | ||
<code> | <code>MethodPatch | PropertyPatcch</code> A patch object for the specified class and method or getter/setter property. See below for usage. | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.patch(Skill, 'addXP'); // Returns a MethodPatch | ||
ctx.patch(Skill, 'level'); // Returns a PropertyPatch</syntaxhighlight> | |||
==== | ==== MethodPatch.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 <code>undefined</code>), the arguments are left as-is. | 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 <code>undefined</code>), the arguments are left as-is. | ||
Line 675: | Line 802: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// Double all XP gains | ||
ctx.patch(Skill, 'addXP').before((amount, masteryAction) => [amount * 2, masteryAction]);</ | ctx.patch(Skill, 'addXP').before((amount, masteryAction) => [amount * 2, masteryAction]);</syntaxhighlight> | ||
==== | ==== MethodPatch.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 <code>undefined</code>), the return value is left as-is. | 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 <code>undefined</code>), the return value is left as-is. | ||
Line 688: | Line 815: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// The player never misses an attack | ||
ctx.patch(Player, 'rollToHit').after(willHit => { | ctx.patch(Player, 'rollToHit').after(willHit => { | ||
if (!willHit) console.log('A miss? I think not!'); | if (!willHit) console.log('A miss? I think not!'); | ||
return true; | return true; | ||
});</ | });</syntaxhighlight> | ||
==== | ==== MethodPatch.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 <code>before</code> and <code>after</code> hooks, respectively. | 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 <code>before</code> and <code>after</code> hooks, respectively. | ||
Line 704: | Line 831: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.patch(Skill, 'addXP').replace(function(o, amount, masteryAction) { | ||
// Prevent any woodcutting XP | // Prevent any woodcutting XP | ||
if (this.id === 'melvorD:Woodcutting') return; | if (this.id === 'melvorD:Woodcutting') return; | ||
Line 713: | Line 840: | ||
// Grant all other XP as normal | // Grant all other XP as normal | ||
return o(amount, masteryAction); | return o(amount, masteryAction); | ||
});</ | });</syntaxhighlight> | ||
It's important to note that using the <code>replace</code> method replaces the current method body, meaning multiple calls of the <code>replace</code> method get executed in the reverse order that they were declared: | It's important to note that using the <code>replace</code> method replaces the current method body, meaning multiple calls of the <code>replace</code> method get executed in the reverse order that they were declared: | ||
< | <syntaxhighlight lang="js" line>const xpPatch = ctx.patch(Skill, 'addXP'); | ||
xpPatch.replace((o, amount, masteryAction) => { | xpPatch.replace((o, amount, masteryAction) => { | ||
Line 732: | Line 859: | ||
// Logs: | // Logs: | ||
// Replacement #2 | // Replacement #2 | ||
// Replacement #1</ | // Replacement #1</syntaxhighlight> | ||
=== | ==== PropertyPatch.get(getter: (o: () => any) => any): void ==== | ||
Execute the provided function and return the return value when a getter property is accessed. | |||
'''Parameters''' | '''Parameters''' | ||
<code> | <code>getter: (o: () => any) => any</code> The getter function to be executed. The parameter <code>o</code> is a reference to the getter method being replaced, which is either a previous getter patch or the original getter method. | ||
'''Example''' | |||
<code> | <syntaxhighlight lang="js" line>// Effectively double available Township resources | ||
ctx.patch(TownshipResource, 'amount').get((o) => o() * 2); | |||
// Or more practically, make resources unlimited | |||
ctx.patch(TownshipResource, 'amount').get(() => 999999);</syntaxhighlight> | |||
==== PropertyPatch.set(setter: (o: (value: any) => void, value: any) => void): void ==== | |||
Execute the provided function when a setter property is accessed. | |||
'''Parameters''' | |||
<code>setter: (o: (value: any) => void, value: any) => void</code> The setter function to be executed. The first parameter, <code>o</code>, is a reference to the setter method being replaced, which is either a previous setter patch or the original setter method. The second parameter, <code>value</code>, contains the value being set. | |||
'''Example''' | |||
<syntaxhighlight lang="js" line>// Sorry, there aren't many setters in the game to use for a practical example | |||
// Doubles whatever resource amount is being set | |||
ctx.patch(TownshipResource, 'amount').set((o, amount) => o(amount * 2)); | |||
// While in-game | |||
game.township.resources.getObjectByID('melvorF:Wood').amount = 1000; | |||
game.township.renderQueue.resourceAmounts = true; | |||
// 2000 wood is available</syntaxhighlight> | |||
==== PropertyPatch.replace(getter?: (o: () => any) => any, setter?: (o: (value: any) => void, value: any) => void): void ==== | |||
Alias for calling <code>get</code> and <code>set</code> at the same time. | |||
'''Parameters''' | |||
<code>getter: (o: () => any) => any</code> See above Parameters for <code>get</code>. | |||
<code>setter: (o: (value: any) => void, value: any) => void</code> See above Parameters for <code>set</code>. | |||
'''Example''' | |||
See above examples for <code>get</code> and <code>set</code>. | |||
=== isPatched(className: class, methodOrPropertyName: string): boolean === | |||
Checks whether or not a method or getter/setter property has been patched. | |||
'''Parameters''' | |||
<code>className: class</code> Class containing the method or property to check for having been patched. Should be the actual class reference, not a string, e.g. <code>Skill</code>, not <code>'Skill'</code>. | |||
<code>methodOrPropertyName: string</code> Name of the method or property to check. | |||
'''Returns''' | '''Returns''' | ||
<code>boolean</code> Whether or not the given class method is patched. | <code>boolean</code> Whether or not the given class method or property is patched. | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>ctx.isPatched(Skill, 'addXP'); // false | ||
ctx.patch(Skill, 'addXP'); | ctx.patch(Skill, 'addXP'); | ||
ctx.isPatched(Skill, 'addXP'); // true</ | ctx.isPatched(Skill, 'addXP'); // true</syntaxhighlight> | ||
== Exposing Properties and Methods (Mod API) == | == Exposing Properties and Methods (Mod API) == | ||
Line 772: | Line 946: | ||
'''Example''' | '''Example''' | ||
< | <syntaxhighlight lang="js" line>// manifest.json | ||
{ | { | ||
"namespace": "helloWorld", | "namespace": "helloWorld", | ||
"setup": "setup.mjs" | "setup": "setup.mjs" | ||
}</ | }</syntaxhighlight> | ||
<small>''Comments in JSON are purely illustrative and not valid markup''</small> | |||
< | <syntaxhighlight lang="js" line>// setup.mjs | ||
export function setup({ api }) { | export function setup({ api }) { | ||
api({ | api({ | ||
greet: name => console.log(`Hello, ${name!}`); | greet: name => console.log(`Hello, ${name!}`); | ||
}); | }); | ||
}</ | }</syntaxhighlight> | ||
Other mods would then be able to interact with your API: | Other mods would then be able to interact with your API: | ||
< | <syntaxhighlight lang="js" line>// some other mod | ||
mod.api.helloWorld.greet('Melvor'); // Hello, Melvor!</ | mod.api.helloWorld.greet('Melvor'); // Hello, Melvor!</syntaxhighlight> | ||
{{ModGuideNav}} | |||
{{Menu}} |
edits