Let's take a look at the Boilerplate System's /module/boilerplate.mjs
file. We'll look at each section of it to see what's happening:
// Import document classes.
import { BoilerplateActor } from "./documents/actor.mjs";
import { BoilerplateItem } from "./documents/item.mjs";
// Import sheet classes.
import { BoilerplateActorSheet } from "./sheets/actor-sheet.mjs";
import { BoilerplateItemSheet } from "./sheets/item-sheet.mjs";
// Import helper/utility classes and constants.
import { preloadHandlebarsTemplates } from "./helpers/templates.mjs";
import { BOILERPLATE } from "./helpers/config.mjs";
In this first section, we've imported code from 6 different ES modules, one each for the Actor and Item document classes, and one each for the ActorSheet and ItemSheet sheet classes, one for Handlebars templates, and one for constants that are used later. For this system, those classes have Boilerplate
as a prefix for them, but you should use a name more appropriate to your system when creating your own.
Directory Structure
We'll review the recommended directory structure in detail later, but at a high level, the following structure is assumed in this tutorial for our ES modules:
/module/documents
- Classes for actor and item documents
/module/helpers
- Utility classes and helper methods
/module/sheets
- Classes for actor and item sheets
Importing doesn't necessarily do anything at this point, but we do have the classes available in our file so that we can now let Foundry know how to use them.
Foundry has a very robust hooks system that lets you hook into different kinds of events, such as init
, ready
, or other hooks for chat messages, scene render, etc. In this case, we'll be using the init
hook to initialize the important overrides in our system. In the example below, everything should be considered essential for your system's init hook.
This example includes comments behind //
that explain more about what's actually happening.
/* -------------------------------------------- */
/* Init Hook */
/* -------------------------------------------- */
Hooks.once('init', function () {
// Add utility classes to the global game object so that they're more easily
// accessible in global contexts.
game.boilerplate = {
BoilerplateActor,
BoilerplateItem,
rollItemMacro,
};
// Add custom constants for configuration.
CONFIG.BOILERPLATE = BOILERPLATE;
/**
* Set an initiative formula for the system
* @type {String}
*/
CONFIG.Combat.initiative = {
formula: '1d20 + @abilities.dex.mod',
decimals: 2,
};
// Define custom Document classes
CONFIG.Actor.documentClass = BoilerplateActor;
CONFIG.Item.documentClass = BoilerplateItem;
// Active Effects are never copied to the Actor,
// but will still apply to the Actor from within the Item
// if the transfer property on the Active Effect is true.
CONFIG.ActiveEffect.legacyTransferral = false;
// Register sheet application classes
Actors.unregisterSheet('core', ActorSheet);
Actors.registerSheet('boilerplate', BoilerplateActorSheet, {
makeDefault: true,
label: 'BOILERPLATE.SheetLabels.Actor',
});
Items.unregisterSheet('core', ItemSheet);
Items.registerSheet('boilerplate', BoilerplateItemSheet, {
makeDefault: true,
label: 'BOILERPLATE.SheetLabels.Item',
});
// Preload Handlebars templates.
return preloadHandlebarsTemplates();
});
We're doing a few things in here:
boilerplate
object on the global game
object that Foundry itself provides so that we more easily have access to some of the classes defined in our ES modules. These are useful for things like debugging in your browser's console or letting modules interact with your system's classes.config.mjs
ES module, you can add them to the global CONFIG
object like in this example.CONFIG.Combat.initiative
object like in this example.Foundry ships with a LOT of core helpers on top of what Handlebars natively provides, but sometimes you need your own.
/* -------------------------------------------- */
/* Handlebars Helpers */
/* -------------------------------------------- */
// If you need to add Handlebars helpers, here is a useful example:
Handlebars.registerHelper('toLowerCase', function (str) {
return str.toLowerCase();
});
This is a fairly simple example - you can call it with {{toLowerCase "The Quick Brown Fox"}}
and the finished HTML will output the quick brown fox
. As with other helpers you can provide variables as arguments in place of static strings and/or combine it with other helpers such as {{toLowerCase (concat foo bar)}}
to return the value of concat(foo, bar).toLowerCase()
.
Much like the init
hook, Foundry also provides a ready
hook that happens after systems and modules have finished initializing. It's useful for steps that need to happen later in the process.
/* -------------------------------------------- */
/* Ready Hook */
/* -------------------------------------------- */
Hooks.once("ready", function() {
// Include steps that need to happen after Foundry has fully loaded here.
});
As with previous examples, this sample code using the Boilerplate System. You should rename your classes such as MySystemNameActor
instead of BoilerplateActor
, and you'll want to update the registerSheet()
lines to replace boilerplate
with mysystemname
, using your system's machine name.
Now let's take a look at the extended Actor class.