Mod Creation/Mod Context API Reference: Difference between revisions

→‎MethodPatch.after(hook: (returnValue: any, ...args: any) => any | void): void: Make it more obvious that the after function has one more argument than the original call (the return type)
(→‎MethodPatch.after(hook: (returnValue: any, ...args: any) => any | void): void: Make it more obvious that the after function has one more argument than the original call (the return type))
 
(15 intermediate revisions by 2 users not shown)
Line 36: Line 36:


  <syntaxhighlight lang="js" line>const devCtx = mod.getDevContext();</syntaxhighlight>
  <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 104: Line 118:
// my-independent-script.js has NOT run yet</syntaxhighlight>
// 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'''
Line 171: Line 189:
   "namespace": "helloMelvor"
   "namespace": "helloMelvor"
}</syntaxhighlight>
}</syntaxhighlight>
<small>''Comments in JSON are purely illustrative and not valid markup''</small>


  <syntaxhighlight lang="js" line>// in JavaScript
  <syntaxhighlight lang="js" line>// in JavaScript
Line 201: Line 221:
=== onCharacterSelectionLoaded(callback: (ctx: ModContext) => void | Promise<void>): void ===
=== onCharacterSelectionLoaded(callback: (ctx: ModContext) => void | Promise<void>): void ===


Execute code after the the character selection screen has fully loaded.
Execute code after the character selection screen has fully loaded.


'''Parameters'''
'''Parameters'''
Line 277: Line 297:
}</syntaxhighlight>
}</syntaxhighlight>


  <nowiki>await ctx.gameData.addPackage('data.json');</nowiki>
<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 ===
=== buildPackage(builder: (packageBuilder: GameDataPackageBuilder) => void): BuiltGameDataPackage ===
Line 311: Line 333:


== 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 410: Line 434:
   // ...
   // ...
}</syntaxhighlight>
}</syntaxhighlight>
<small>''Comments in JSON are purely illustrative and not valid markup''</small>


  <syntaxhighlight lang="js" line>ctx.settings.type('customText', {
  <syntaxhighlight lang="js" line>ctx.settings.type('customText', {
Line 449: 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 619: Line 646:


== 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 680: Line 709:


== 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 741: Line 772:


== Game Object Patching/Hooking ==
== Game Object Patching/Hooking ==
=== A Quick Note on Function Syntax ===
When patching methods, for most scenarios you'll want to use a traditional function expression, rather than the arrow expression syntax. This will ensure <code>this</code> is bound to the class instance that is calling the method, rather than the context where the patch was defined.
For example,
<syntaxhighlight lang="js" line="1">export function setup({ patch }) {
  const methodPatch = patch(Class, 'method');
  // Do this
  methodPatch.before(function () { });
 
  // Or this
  function beforePatch () { }
  methodPatch.before(beforePatch);
 
  // Not this, unless you understand the implications of doing so
  methodPatch.before(() => { });
}</syntaxhighlight>


=== patch(className: class, methodOrPropertyName: string): MethodPatch | PropertyPatch ===
=== patch(className: class, methodOrPropertyName: string): MethodPatch | PropertyPatch ===
Line 772: Line 821:


  <syntaxhighlight lang="js" line>// Double all XP gains
  <syntaxhighlight lang="js" line>// Double all XP gains
ctx.patch(Skill, 'addXP').before((amount, masteryAction) => [amount * 2, masteryAction]);</syntaxhighlight>
ctx.patch(Skill, 'addXP').before(function (amount, masteryAction) {
  return [amount * 2, masteryAction];
});</syntaxhighlight>


==== MethodPatch.after(hook: (returnValue: any, ...args: any) => any | void): void ====
==== MethodPatch.after(hook: (returnValue: any, ...args: any) => any | void): void ====
Line 785: Line 836:


  <syntaxhighlight lang="js" line>// The player never misses an attack
  <syntaxhighlight lang="js" line>// The player never misses an attack
ctx.patch(Player, 'rollToHit').after(willHit => {
// Patching: rollToHit(target: Character, attack: SpecialAttack): boolean;
   if (!willHit) console.log('A miss? I think not!');
ctx.patch(Player, 'rollToHit').after(function(willHit, target, attack) {
   if (!willHit) {
    console.log(`A miss? With ${attack.name}? Against ${target.noun.plain}? I think not!`);
  }
   return true;
   return true;
});</syntaxhighlight>
})</syntaxhighlight>


==== MethodPatch.replace(replacement: (replacedMethod: (...args: any) => any, ...args: any) => any): void ====
==== MethodPatch.replace(replacement: (replacedMethod: (...args: any) => any, ...args: any) => any): void ====
Line 800: Line 854:
'''Example'''
'''Example'''


  <syntaxhighlight lang="js" line>ctx.patch(Skill, 'addXP').replace(function(o, amount, masteryAction) {
  <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 815: Line 869:
  <syntaxhighlight lang="js" line>const xpPatch = ctx.patch(Skill, 'addXP');
  <syntaxhighlight lang="js" line>const xpPatch = ctx.patch(Skill, 'addXP');


xpPatch.replace((o, amount, masteryAction) => {
xpPatch.replace(function (o, amount, masteryAction) {
   console.log('Replacement #1');
   console.log('Replacement #1');
   return o(amount, masteryAction);
   return o(amount, masteryAction);
});
});


xpPatch.replace({o, amount, masteryAction) => {
xpPatch.replace(function (o, amount, masteryAction) {
   console.log('Replacement #2');
   console.log('Replacement #2');
   return o(amount, masteryAction);
   return o(amount, masteryAction);
Line 841: Line 895:


  <syntaxhighlight lang="js" line>// Effectively double available Township resources
  <syntaxhighlight lang="js" line>// Effectively double available Township resources
ctx.patch(TownshipResource, 'amount').get((o) => o() * 2);
ctx.patch(TownshipResource, 'amount').get(function (o) {
  return o() * 2;
});
// Or more practically, make resources unlimited
// Or more practically, make resources unlimited
ctx.patch(TownshipResource, 'amount').get(() => 999999);</syntaxhighlight>
ctx.patch(TownshipResource, 'amount').get(function () {
  return 999999;
});</syntaxhighlight>


==== PropertyPatch.set(setter: (o: (value: any) => void, value: any) => void): void ====
==== PropertyPatch.set(setter: (o: (value: any) => void, value: any) => void): void ====
Line 857: Line 915:
  <syntaxhighlight lang="js" line>// Sorry, there aren't many setters in the game to use for a practical 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
// Doubles whatever resource amount is being set
ctx.patch(TownshipResource, 'amount').set((o, amount) => o(amount * 2));
ctx.patch(TownshipResource, 'amount').set(function (o, amount) {
  return o(amount * 2);
});
// While in-game
// While in-game
game.township.resources.getObjectByID('melvorF:Wood').amount = 1000;
game.township.resources.getObjectByID('melvorF:Wood').amount = 1000;
Line 920: Line 980:
   "setup": "setup.mjs"
   "setup": "setup.mjs"
}</syntaxhighlight>
}</syntaxhighlight>
<small>''Comments in JSON are purely illustrative and not valid markup''</small>


  <syntaxhighlight lang="js" line>// setup.mjs
  <syntaxhighlight lang="js" line>// setup.mjs
56

edits