Mod Creation/Mod Context API Reference: Difference between revisions

From Melvor Idle
(API reference guide for the mod context object.)
 
No edit summary
Line 87: Line 87:
ctx.loadScript('my-independednt-script.js');
ctx.loadScript('my-independednt-script.js');
// my-independent-script.js has NOT run yet</nowiki>
// my-independent-script.js has NOT run yet</nowiki>
=== <code>loadTemplates(path: string): void</code> ===
Inject all <template> elements contained in a given HTML file into the document body.
'''Parameters'''
<code>path: string</code> The relative path to the HTML resource
'''Example'''
<nowiki>ctx.loadTemplates('my-templates.html');</nowiki>


=== <code>loadStylesheet(path: string): void</code> ===
=== <code>loadStylesheet(path: string): void</code> ===
Line 548: Line 558:
'''Example'''
'''Example'''
  <nowiki>ctx.accountStorage.clear();</nowiki>
  <nowiki>ctx.accountStorage.clear();</nowiki>
== Game Method Patching/Hooking ==
=== <code>patch(className: class, methodName: string): Patch</code> ===
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'''
<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>methodName: string</code> Name of the method to patch.
'''Returns'''
<code>Patch</code> A patch object for the specified class and method. See below for usage.
'''Example'''
<nowiki>ctx.patch(Skill, 'addXP');</nowiki>
==== <code>Patch.before(hook: (...args: any) => any[] | void): void</code> ====
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.
'''Parameters'''
<code>hook: (...args: any) => any[] | void</code> The callback hook to be executed.
'''Example'''
<nowiki>// Double all XP gains
ctx.patch(Skill, 'addXP').before((amount, masteryAction) => [amount * 2, masteryAction]);</nowiki>
==== <code>Patch.after(hook: (returnValue: any, ...args: any) => any | void): void</code> ====
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.
'''Parameters'''
<code>hook: (returnValue: any, ...args: any) => any | void</code> The callback hook to be executed.
'''Example'''
<nowiki>// The player never misses an attack
ctx.patch(Player, 'rollToHit').after(willHit => {
  if (!willHit) console.log('A miss? I think not!');
  return true;
});</nowiki>
==== <code>Patch.replace(replacement: (replacedMethod: (...args: any) => any, ...args: any) => any): void</code> ====
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.
'''Parameters'''
<code>replacement: (replacedMethod: (...args: any) => any, ...args: any) => any</code> The callback function to replace the method body.
'''Example'''
<nowiki>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);
});</nowiki>
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:
<nowiki>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</nowiki>
=== <code>isPatched(className: class, methodName: string): boolean</code> ===
Checks whether or not a method has been patched.
'''Parameters'''
<code>className: class</code> Class containing the method 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>methodName: string</code> Name of the method to check.
'''Returns'''
<code>boolean</code> Whether or not the given class method is patched.
'''Example'''
<nowiki>ctx.isPatched(Skill, 'addXP'); // false
ctx.patch(Skill, 'addXP');
ctx.isPatched(Skill, 'addXP'); // true</nowiki>
== 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 <code>api</code> method on the context object. After defining an API using the method below, other mods can access it through the global <code>mod.api['your_mods_namespace']</code> object.
=== <code>api(endpoints?: object): object</code> ===
Specify properties and methods to expose on the global <code>mod.api['your_mods_namespace']</code> object. Can be called multiple times to append more endpoints.
'''Parameters'''
<code>endpoint: object</code> An object containing any properties or methods you want to expose. Can be omitted to just retrieve your mod's current API object.
'''Returns'''
<code>object</code> The mod's API object
'''Example'''
<nowiki>// manifest.json
{
  "namespace": "helloWorld",
  "setup": "setup.mjs"
}</nowiki>
<nowiki>// setup.mjs
export function setup({ api }) {
  api({
    greet: name => console.log(`Hello, ${name!}`);
  });
}</nowiki>
Other mods would then be able to interact with your API:
<nowiki>// some other mod
mod.api.helloWorld.greet('Melvor'); // Hello, Melvor!</nowiki>