foundry-vtt-module-dev について
Foundry Virtual Tabletop v14以降のモジュール開発・保守を支援するスキルです。構成、ライフサイクル、データモデル、UI、スタイル、移行を扱います。
主な機能
- 01v14モジュール構成とマニフェスト
- 02初期化ライフサイクルの解説
- 03名前空間APIリファレンス
- 04ドキュメントモデルとサブタイプ
- 05ApplicationV2とCSSスタイル
活用シーン
- 01Foundry VTTモジュールの新規作成
- 02モジュールのv14 APIへの移行
- 03カスタムドキュメント型の追加
- 04テーマ対応モジュールUIの実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ fcsouza/ agent-skills --skill foundry-vtt-module-devインストール後、Claude Code で/foundry-vtt-module-devを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
boilerplate/actor-sheet.mjsmain.mjsmodule.jsonpackage.jsonrollup.config.mjssheet.hbssocket-handler.mjstype-data-model.mjsvite.config.mjsreferences/accessibility.mdactive-effects-v2.mdadventure-documents.mdapplication-v2.mdaudio-and-macros.mdbuild-pipeline.mdcanvas-and-pixi.mdchat-and-ui.mdcombat-and-tokens.mddocument-model.mdhandlebars-and-templates.mdhooks-and-settings.mdmeasured-templates.mdmigration-guide.mdmodule-subtypes.mdpermissions-and-ownership.mdregions-and-grid.mdscene-levels.mdsockets-rolls-packs.mdtesting-with-quench.mdv14-migration.md詳細
Foundry VTT モジュール開発
Foundry Virtual Tabletop(v14 以降)向けのモジュールを作成、拡張、保守します。このスキルでは、新しいモジュールのひな形作成から Foundry のバージョン間の移行まで、モジュールのライフサイクル全体を扱います。v13 から移行する場合は、最初に references/v14-migration.md をお読みください。
クイックスタート
モジュールの構成
my-module/
├── module.json ← manifest (required)
├── scripts/
│ └── main.mjs ← ES module entry point
├── templates/ ← Handlebars HTML templates
├── styles/ ← CSS stylesheets
├── packs/ ← compendium data
└── lang/
└── en.json ← localization strings
出発点として boilerplate/module.json と boilerplate/main.mjs を使用してください。
モジュールマニフェスト(module.json)
すべてのモジュールには、有効な module.json が必要です。v14 の重要なフィールドは次のとおりです。
{
"id": "my-module",
"title": "My Module",
"description": "What this module does.",
"version": "1.0.0",
"compatibility": {
"minimum": "14",
"verified": "14"
},
"documentTypes": { "Actor": { "hero": {} } },
"authors": [{ "name": "Your Name", "url": "https://github.com/you" }],
"esmodules": ["scripts/main.mjs"],
"styles": [{ "src": "styles/my-module.css", "layer": "modules" }],
"languages": [{ "lang": "en", "name": "English", "path": "lang/en.json" }],
"socket": true,
"relationships": {
"systems": [],
"requires": [],
"recommends": []
}
}
| フィールド | 用途 |
|---|---|
id | 一意の識別子。使用できる文字は [A-Za-z0-9_-] のみです。フォルダー名と一致させる必要があります |
compatibility | minimum(このバージョン未満では読み込まれません)、verified(テスト済みのバージョン)を指定します。互換性がないことが確認されている場合を除き、maximum は省略してください |
documentTypes | モジュールが登録するカスタムの Actor/Item/ActiveEffect サブタイプを宣言します。キーは CONFIG.<Doc>.dataModels のキーと一致させる必要があります |
esmodules | ES モジュールのエントリーポイントです。従来の scripts より常にこちらを優先してください |
styles | { src, layer? } オブジェクトの配列です。layer はシートを読み込む CSS カスケードレイヤーの名前です。通常の文字列も引き続き移行されますが、オブジェクトを記述してください |
socket | モジュールで game.socket.emit/on を有効にするには true に設定します |
packs | コンペンディウムパック定義の配列です。name は [A-Za-z0-9_-] に一致する必要があります。名前やパスが重複すると、読み込み時にエラーが発生します |
relationships.requires | 他のモジュールやシステムへの必須依存関係です |
library | このモジュールがユーザー向けではなく共有ライブラリである場合は true に設定します |
quickstart | v14 で追加されました。{ adventures: { "<id>": { uuid } }, postImport, world: { background, cover, description } } を指定すると、モジュールがクイックスタートとしてマークされ、セットアップ画面からワールドを作成してアドベンチャーをインポートします |
v14 での変更点: システムでも template.json は非推奨になりました(v16 まで)。型は documentTypes と TypeDataModel で宣言してください。静的な .html ファイルは text/plain として配信されます。テンプレートは引き続きレンダリングされますが、ブラウザーで直接開くことはできません。
初期化ライフサイクル
モジュールは、順番に実行される 3 つのフックを通じて動作します。エントリーポイントでフックを登録してください。
// init — register settings, sheets, custom document types
// game.user is NOT available yet. Canvas is NOT ready.
Hooks.once("init", () => {
console.log("my-module | Initializing");
// Register settings, custom sheets, document types here
});
// setup — packages loaded, documents available, canvas not ready
Hooks.once("setup", () => {
// Modify CONFIG, register additional features
});
// ready — everything available: game.actors, game.scenes, canvas
Hooks.once("ready", () => {
console.log("my-module | Ready");
// Safe to access game.actors, game.scenes, game.user
// Run migrations, initialize socket listeners
});
CONFIG の追加(CONFIG.statusEffects、CONFIG.ActiveEffect.changeTypes、CONFIG.Canvas.layers)は init で登録してください。v14 以降、game.template はなくなりました。型のデフォルト値は game.model または TypeDataModel のスキーマから読み取ってください。
スタイル設定(CSS カスケードレイヤー)
Foundry は CSS カスケードレイヤー(@layer)を使用します。特異性の競合を避け、Foundry のライト/ダークテーマをサポートできるよう、モジュールの CSS をレイヤーに配置してください。コアでは、レイヤーが次の順序で宣言されています:reset, variables, elements, blocks, applications, compatibility, layouts, system, modules, exceptions。マニフェストで modules を指定すると、styles: [{ "src": "...", "layer": "modules" }]、ルールはコア独自のルールの後、exceptions の前に配置されます。
レイヤー名を独自に作らないでください。コアで宣言されていない名前は exceptions の後に配置され、コアの CSS をすべて上書きします。
modules の下で読み込まれるスタイルシート内にネストした @layer を置くと、そのレイヤー内にネストされます。そのため、@layer my-module { ... } は modules.my-module となり、レイヤー順序内の位置は変わりません。独自のルールをまとめるために使用し、マニフェストのフィールドの代わりにはしないでください。
@layer my-module {
.my-module .window-content {
--accent-color: var(--color-warm-2);
padding: 0.5rem;
}
}
テーマ設定では、シート上の body.theme-light/body.theme-dark と .themed.theme-<x> を使用します。@layer の名前とその仕組みは v13 から変更されていません。
CSS 変数(テーマ対応スタイル)
Foundry は、ライト/ダークテーマに対応する CSS カスタムプロパティを提供しています。ハードコードされた色よりも、常にこれらを優先してください。
@layer my-module {
.my-module-panel {
/* Text */
color: var(--color-text-primary);
border: 1px solid var(--color-border);
/* Accent palette */
--my-accent: var(--color-warm-2);
--my-muted: var(--color-cool-4);
/* Typography */
font-family: var(--font-primary);
font-size: var(--font-size-13);
}
}
主な変数のカテゴリ:
| カテゴリ | 変数 |
|---|---|
| テキスト | --color-text-primary, --color-text-secondary, --color-text-emphatic, --color-text-subtle, --color-text-dark-primary, --color-text-dark-secondary, --color-text-hyperlink, --color-text-selection |
| ボーダー | --color-border, --color-border-dark, --color-border-light-1, --color-border-light-2 |
| 暖色系アクセント | --color-warm-1, --color-warm-2, --color-warm-3 |
| 寒色系アクセント | --color-cool-3, --color-cool-4, --color-cool-5 |
| グレー | --color-dark-1 … --color-dark-6, --color-light-1 … --color-light-6 |
| フォント | --font-primary, --font-body, --font-sans, --font-h1, --font-size-13 ~ --font-size-48 |
| カーソル(v14 で追加) | --cursor-default, --cursor-pointer, --cursor-grab, --cursor-text(および -down バリアント) |
ローカル開発
- Foundry のデータパスにモジュールフォルダーを作成します:
{userData}/Data/modules/my-module/ - またはシンボリックリンクを作成します:
ln -s /path/to/your/dev/folder {userData}/Data/modules/my-module - Foundry を起動し、アドオンモジュールに移動して、ワールド内でモジュールを有効にします
- ブラウザーのコンソール(F12)を開いて、ログとエラーを確認します
名前空間(foundry.*)
v13 以降、コア API のほぼすべてが foundry.* 名前空間に置かれています。レガシーなグローバル名も非推奨化用シムを通じて引き続き解決されますが、v15 で削除され(appv1 フレームワークは v16 で削除)、v14 ではすでに v12 時代のシムが削除されています。mergeObject、getProperty、Die、DiceTerm、Math.clamped、CONST.DOCUMENT_TYPES などの裸の名前は使えません。新しいファイルではすべて名前空間付きのパスを使用し、既存のファイルも編集する際に更新してください。
| レガシーなグローバル名(v12 以前) | 名前空間付きのパス |
|---|---|
Application, FormApplication | foundry.appv1.api.Application (代わりに V2 を使用) |
Dialog | foundry.appv1.api.Dialog (DialogV2 を使用) |
ApplicationV2 | foundry.applications.api.ApplicationV2 |
HandlebarsApplicationMixin | foundry.applications.api.HandlebarsApplicationMixin |
DialogV2 | foundry.applications.api.DialogV2 |
DocumentSheetV2 | foundry.applications.api.DocumentSheetV2 |
ActorSheetV2, ItemSheetV2 | foundry.applications.sheets.ActorSheetV2 / .ItemSheetV2 |
Hooks | foundry.helpers.Hooks (グローバルの Hooks も引き続きエイリアスとして使用可能) |
Canvas, CanvasLayer | foundry.canvas.Canvas, foundry.canvas.layers.CanvasLayer |
Token, Tile, Region(オブジェクト) | foundry.canvas.placeables.*(MeasuredTemplate は v14 以降非推奨です。Region を使用してください) |
Document, DataModel, TypeDataModel | foundry.abstract.Document, foundry.abstract.DataModel, foundry.abstract.TypeDataModel |
fields.*(NumberField など) | foundry.data.fields.* |
Roll, DiceTerm, Die | foundry.dice.Roll, foundry.dice.terms.Die |
Actors, Items(コレクション) | foundry.documents.collections.Actors / .Items |
loadTemplates, renderTemplate | foundry.applications.handlebars.loadTemplates / .renderTemplate |
| (レガシーなエイリアスなし) | foundry.applications.fields.* — createFormGroup、createSelectInput、createCheckboxInput、createNumberInput、createTextInput、createTextareaInput、createMultiSelectInput、createEditorInput、setInputAttributes |
| (レガシーなエイリアスなし) | foundry.applications.ux.* — Tabs、ContextMenu、DragDrop、Draggable、FormDataExtended、HTMLSecret、ProseMirrorEditor、SearchFilter、TextEditor |
mergeObject, duplicate, debounce, isNewerVersion | foundry.utils.*(v14 で裸のグローバル名は削除されています。mergeObject、deepClone、expandObject、flattenObject、getProperty、setProperty、hasProperty、diffObject、equals(objectsEqual の代替)、getType、isEmpty、isNewerVersion、randomID、debounce、throttle、benchmark、parseUuid、buildUuid、buildRelativeUuid、escapeHTML、formatFileSize など) |
| (レガシーなエイリアスなし) | foundry.data.operators.* — ForcedDeletion、ForcedReplacement(グローバルの _del、_replace)は、更新キー -= / == の代わりに使用します |
更新する理由は? 残りのシムは v15 で削除される予定です(appv1 は v16)。名前空間付きのパスを使って書いたコードは将来も互換性がありますが、レガシーなグローバル名を使って書いたコードは、いつ使えなくなってもおかしくありません。
簡単な分割代入のパターン:
const { ApplicationV2, HandlebarsApplicationMixin, DialogV2 } = foundry.applications.api;
const { ActorSheetV2 } = foundry.applications.sheets;
const fields = foundry.data.fields;
各ファイルの先頭でこれらを使用すると、ファイルの残りの部分を自然に記述できます。
Document モデル
Foundry のデータレイヤーは、DataModel と Document を基盤としています。モジュールはこれを拡張して、カスタム Actor タイプや Item タイプを作成したり、構造化データを保存したりできます。
基本パターン: 型付きフィールドを含むスキーマを定義 → init 中に CONFIG に登録 → Foundry が永続化、検証、同期を処理します。
class HeroData extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
health: new fields.NumberField({ required: true, initial: 100, min: 0 }),
class: new fields.StringField({ required: true, initial: "fighter" }),
abilities: new fields.SchemaField({
strength: new fields.NumberField({ initial: 10 }),
dexterity: new fields.NumberField({ initial: 10 }),
}),
inventory: new fields.ArrayField(new fields.StringField()),
};
}
prepareDerivedData() {
this.maxHealth = this.health + this.abilities.strength * 2;
}
}
// Register in init hook
Hooks.once("init", () => {
CONFIG.Actor.dataModels.hero = HeroData;
});
フラグは、あらゆるドキュメントに付与できるモジュール名前空間付きのメタデータです。安全で、モジュールをアンインストールしても保持されます。
await actor.setFlag("my-module", "customData", { tracked: true });
const data = actor.getFlag("my-module", "customData");
await actor.unsetFlag("my-module", "customData");
v14 での変更 — 更新演算子。 "-=key": null および "==key": value の更新構文は非推奨です(v16 まで)。foundry.data.operators(グローバル変数 _del および _replace)を使用してください。
await actor.update({ "system.inventory": _del }); // delete the key
await actor.update({ "system.abilities": _replace({ str: 12 }) }); // replace without merging
ActiveEffect は型付きドキュメントになりました(type + system。サブタイプは documentTypes.ActiveEffect 経由)。その changes は system.changes に格納されます。詳しくは Active Effects のセクションを参照してください。DataField#migrateSource は _migrate(value, options, _state) に変更され、migrateData はデータを返す必要があります。
フィールドタイプの全リファレンス、ライフサイクルフック(_preCreate、_onCreate、_preUpdate、_onUpdate、_preDelete、_onDelete)、埋め込みドキュメントの管理、複数ドキュメントへのアトミックな書き込みを行う foundry.documents.modifyBatch()、フラグとモデルフィールドの使い分けについては、references/document-model.md を参照してください。
モジュールのサブタイプ
モジュールは、あらゆるシステムで動作するあらゆるワールドに、カスタムの Actor、Item、JournalEntryPage、その他のドキュメントサブタイプを追加できます。v11 以降、公式にサポートされている拡張メカニズムです。
必要な要素は 3 つあります。module.json の documentTypes 内で宣言し(htmlFields と filePathFields を含めます)、自動付与されるプレフィックス付きキー(my-module.vehicle)で CONFIG.<Doc>.dataModels に TypeDataModel を登録し、プレフィックス付きタイプのシートを登録します。モジュールをアンインストールしてもユーザーが困らないよう、必ず変換手段を用意してください。
宣言の構文、無効化時の動作、変換マクロ、注意点については、references/module-subtypes.md を参照してください。
アプリケーションフレームワーク(v2)
すべての UI で ApplicationV2 を使用します。レガシーの Application クラスおよび FormApplication クラス(foundry.appv1)は非推奨で、v16 で削除される予定です。
標準的なパターン: テンプレートを使用するウィンドウには HandlebarsApplicationMixin(ApplicationV2) を継承します。アクターシート/アイテムシートには、foundry.applications.sheets の ActorSheetV2/ItemSheetV2 を使用します。これらは DocumentSheetV2 を継承し、ドキュメント固有のドラッグ&ドロップとトークン管理を追加します。
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
class MySheet extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
id: "my-sheet",
classes: ["my-module"],
window: { title: "My Sheet", resizable: true },
position: { width: 500, height: 400 },
actions: {
rollDice: MySheet.#onRollDice,
},
};
static PARTS = {
main: { template: "modules/my-module/templates/sheet.hbs" },
};
async _prepareContext(options) {
return { name: "Hello Foundry" };
}
static async #onRollDice(event, target) {
const roll = new Roll("1d20");
await roll.evaluate();
await roll.toMessage({ flavor: "Ability Check" });
}
}
v14 の新機能 — 分離ウィンドウ。 すべての ApplicationV2 には、デフォルトで「分離」/「再接続」のヘッダーコントロール(DEFAULT_OPTIONS.window.controls)が追加されます。app.detachWindow() はアプリを独立したブラウザーウィンドウに移動し、app.attachWindow() は元のウィンドウに戻します。app.renderChild(child) は同じウィンドウ内に別のアプリをレンダリングします。各切り替え時に openDetachedWindow(id, win)/closeDetachedWindow(id, win) フックが実行されます。ドキュメント間の移動後も維持する必要があるカスタム要素は、foundry.applications.elements.AdoptableHTMLElement を継承してください。
v14 の新機能 — レンダーフックとフレームボタン。 _preRender(context, options) は _prepareContext の後、フレームとパーツのレンダリング前に実行されます。クラスチェーン内の各クラスに対して preRender<ClassName> フックを呼び出します。_getFrameButtons(options) は追加のヘッダーボタン({ icon, label, action, visible? }。ヘッダーコントロールと同じ形式で、現在は label/visible/onClick を使用します。name/condition/callback は非推奨です)を返します。DialogV2.wait({ renderOptions }) は、ダイアログのレンダー呼び出しにオプションを渡します。
DocumentSheetV2、DialogV2、パーツシステム、アクションハンドラー、フォーム送信については、references/application-v2.md を参照してください。ドラッグ&ドロップの詳細、非同期処理/競合パターン、デバウンス戦略については、同じファイルの「Drag & Drop Deep Dive」および「Async Patterns」セクションを参照してください。
Foundry のカスタム Handlebars ヘルパー({{localize}}、{{selectOptions}}、{{formInput}}、{{formGroup}}、{{editor}} など。{{select}} と {{colorPicker}} は v14 で削除されました)、カスタム HTML 要素(<prose-mirror>、<file-picker>、<color-picker>、<string-tags>、および v14 の <formula-input> と <autocomplete-tags>)、それらをヘルパーより優先すべき場合、カスタムヘルパー/パーシャルの登録、テンプレートの事前読み込み、一般的なシートのパターンについては、references/handlebars-and-templates.md を参照してください。リッチテキストは ProseMirror のみです。TinyMCE と CONFIG.TinyMCE は廃止されました。
シートとダイアログは HTML です。ARIA ロール、キーボード操作、フォーカス管理、および prefers-reduced-motion への対応を加えてください。references/accessibility.md を参照してください。
フックと設定
フックは Foundry のイベントシステムです。設定には、ワールド単位またはクライアント単位のモジュール設定が保存されます。
// Document lifecycle hooks — fire for every Actor/Item/etc CRUD operation
Hooks.on("createActor", (actor, options, userId) => {
console.log(`Actor ${actor.name} created by user ${userId}`);
});
Hooks.on("preUpdateItem", (item, changes, options, userId) => {
// Return false to cancel the update
if (changes.name === "forbidden") return false;
});
// Settings — register in init, use anywhere after
Hooks.once("init", () => {
game.settings.register("my-module", "difficulty", {
name: "Difficulty Level",
hint: "Adjusts the challenge rating of encounters.",
scope: "world", // GM-set, all players see same value
config: true, // show in settings menu
type: String,
choices: { easy: "Easy", normal: "Normal", hard: "Hard" },
default: "normal",
onChange: (value) => console.log("Difficulty changed to", value),
});
});
// Read a setting
const diff = game.settings.get("my-module", "difficulty");
v14 の新しいフック: preRender<App>(app, context, options)、openDetachedWindow/closeDetachedWindow(id, win)、Placeables サイドバータブ用の get<DocumentName>PlaceableContextOptions(app, menuItems)、dropItemSheetData(item, sheet, data)、planToken(document)。削除されたフックはありません。jQuery を使う renderChatMessage は引き続き非推奨で、代わりに renderChatMessageHTML(message, html, context) を使用してください。renderChatMessage は v15 で削除されます。
v14 で変更 — コア設定: core.rollMode は非推奨の互換用設定です。game.settings.get("core", "messageMode")(public | gm | blind | self | ic)を参照してください。core.gridTemplates と core.coneTemplateType は、MeasuredTemplates とともに非推奨になりました。
フックのライフサイクル全体、ドキュメントフックの命名規則、キャンバスフック、Hooks.callAll と Hooks.call の違い、設定サブメニュー、scope: "world" と scope: "client" の違いについては、references/hooks-and-settings.md を参照してください。
高度なパターン
ソケット(GM が権限を持つ方式)
非 GM クライアントは、ワールドドキュメントを直接変更できません。パターンは、クライアントがリクエストを送信し、GM クライアントがそれを受け取って変更を実行し、その結果をブロードキャストする、というものです。
// Requires "socket": true in module.json
const SOCKET_NAME = "module.my-module";
// GM listens and executes
Hooks.once("ready", () => {
game.socket.on(SOCKET_NAME, async (data) => {
if (!game.user.isGM) return;
if (data.type === "updateActor") {
const actor = game.actors.get(data.actorId);
await actor.update(data.changes);
}
});
});
// Any client requests
function requestActorUpdate(actorId, changes) {
if (game.user.isGM) {
return game.actors.get(actorId).update(changes);
}
game.socket.emit(SOCKET_NAME, { type: "updateActor", actorId, changes });
}
ダイスロール
const roll = new Roll("2d6 + @mod", { mod: 3 });
await roll.evaluate(); // ALWAYS await — roll() is only an async alias of evaluate()
await roll.toMessage({ flavor: "Damage Roll" });
console.log(roll.total); // e.g. 11
// Visibility: pass a messageMode (a key of CONFIG.ChatMessage.modes)
await roll.toMessage({ flavor: "Secret" }, { messageMode: "gm" });
await roll.toMessage({ flavor: "Uses the chat dropdown" },
{ messageMode: game.settings.get("core", "messageMode") });
// Use getRollData() on actors to expose system data to roll formulas
const rollData = actor.getRollData(); // { abilities: { str: 16, ... }, health: 50, ... }
const abilityRoll = new Roll("1d20 + @abilities.str", rollData);
v14 で変更 — ロールモードはメッセージモードになりました。 Roll#toMessage、ChatMessage.create、RollTable#draw、Combat#rollInitiative の rollMode(publicroll、gmroll、blindroll、selfroll)は非推奨です。public | gm | blind | self | ic の messageMode を使用してください。CONFIG.Dice.rollModes は CONFIG.ChatMessage.modes に、ChatMessage#applyRollMode は applyMode に置き換えられました。古い値は Roll._mapLegacyRollMode(rollMode) で変換してください。チャットコマンドは ChatLog.CHAT_COMMANDS に登録します(MESSAGE_PATTERNS は非推奨です)。
コンペンディウムパック
const pack = game.packs.get("my-module.monsters");
const docs = await pack.getDocuments();
const dragon = await pack.getDocument("some-id");
await game.actors.importFromCompendium(pack, "some-id");
ローカライズ
// lang/en.json: { "MY_MODULE.greeting": "Hello, {name}!" }
game.i18n.localize("MY_MODULE.greeting"); // "Hello, {name}!"
game.i18n.format("MY_MODULE.greeting", { name: "GM" }); // "Hello, GM!"
v14 では game.i18n.localize にバインドされた _loc グローバルが追加され、localize(id, data) でプレースホルダーもフォーマットされるようになりました。game.i18n.localize は非推奨ではありません。_loc は省略形です。
ソケットのパターン、カスタム DiceTerm、Roll.RESOLVERS、コンペンディウムのクエリ、fromUuid()、ローカライズの設定について詳しくは、references/sockets-rolls-packs.md をお読みください。
キャンバス拡張
PIXI.js を使用してカスタムレイヤーや配置可能オブジェクトを追加し、Foundry キャンバスを拡張します。
const { CanvasLayer } = foundry.canvas.layers;
class MyLayer extends CanvasLayer {
static get layerOptions() {
return foundry.utils.mergeObject(super.layerOptions, { name: "myLayer" });
}
async _draw(options) {
// Add PIXI children here
}
async _tearDown(options) {
this.removeChildren().forEach(c => c.destroy());
}
}
// Register in init
Hooks.once("init", () => {
CONFIG.Canvas.layers.myLayer = { layerClass: MyLayer, group: "primary" };
});
レイヤークラスを差し替える場所は CONFIG.Canvas.layers のみです。v14 以降、CONFIG.<Doc>.layerClass(CONFIG.Token.layerClass など)は非推奨のプロキシです。代わりに CONFIG.Canvas.layers.tokens.layerClass = MyTokenLayer と記述してください。
Scene Levels(v14 で追加)。 Scene には Level ドキュメントの埋め込みコレクション(scene.levels、scene.initialLevel、表示中のレベルを示す canvas.level)が保持されます。背景、前景、フォグのテクスチャは Scene から Level に移動しました(level.background.src、level.foreground.src、level.elevation.{bottom,top})。scene.background、scene.foreground、scene.foregroundElevation、scene.backgroundColor は非推奨のゲッターです。配置可能な各ドキュメントには levels セットがあります。トークンには level と depth があります。非推奨の getDocuments() の代わりに、layer.viewedDocuments()(Level を考慮)を反復処理してください。references/scene-levels.md をお読みください。
テンプレートは Regions に置き換えられました。 MeasuredTemplateDocument、TemplateLayer、Scene#templates は非推奨です(v16 まで)。RegionDocument#shapes では、circle、cone、ellipse、emanation、grid、line、polygon、rectangle、ring、token の各シェイプを使用できます。インタラクティブな配置には canvas.regions.placeRegion(data, { create }) を使用し、canvas.regions.templateMode で切り替えます。
const region = await canvas.regions.placeRegion({
name: "Fireball",
shapes: [{ type: "circle", x: 0, y: 0, radius: canvas.dimensions.distancePixels * 20 }],
levels: [canvas.level.id],
displayMeasurements: true,
});
RegionDocument.createTokenEmanation(token, range, regionData) は、トークンに紐付いたオーラ(attachment.token)を作成します。region.spawnTokens() / region.teleportTokens() はトークンをリージョン内に移動します。references/measured-templates.md の「Templates with Regions」と、references/regions-and-grid.md をお読みください。
Placeables サイドバーとパレット。 v14 では Placeables サイドバータブ(ui.placeables、foundry.applications.sidebar.tabs.PlaceableDirectory)が追加されました。ドキュメントタイプごとにタブがあり(CONFIG.<Doc>.sidebar = { applicationClass, order })、フック get<DocumentName>PlaceableContextOptions を利用できます。Placeable の設定は foundry.applications.sheets.PlaceableConfig を拡張します。
VFX(実験的)。 foundry.canvas.vfx には VFXEffect とコンポーネントが含まれています。CONFIG.Canvas.vfx.enabled = true にするまで無効であり、API は今後変更される可能性があります。foundry.canvas.animation.ParticleGenerator は ParticleEffect / PrimaryParticleEffect に置き換わるものです(v16 まで非推奨)。CanvasShakeEffect は画面を揺らし、Scene#transition はシーンの遷移を制御します(CONFIG.Canvas.sceneTransitions)。
ほとんどのモジュールでキャンバス拡張は必要ありません。カスタムレイヤー、PlaceableObject のサブクラス、座標変換、CanvasAnimation.animate()、PIXI のパフォーマンスに関するヒントについては、references/canvas-and-pixi.md をお読みください。
バンドル済みの JS ライブラリ
Foundry には、以下のライブラリがあらかじめ読み込まれています。インストールもインポートも不要です。
| ライブラリ | グローバル | 用途 | 参照先 |
|---|---|---|---|
| Handlebars | Handlebars | シート/ダイアログ/チャット向けの HTML テンプレート | references/handlebars-and-templates.md |
| PixiJS | PIXI | WebGL キャンバスの描画。トークン、ライティング、シーンを支えるエンジンです(7.4.3 に固定) | references/canvas-and-pixi.md |
| anime.js(v14 で追加) | animejs | トゥイーン/タイムラインアニメーションライブラリ。animejs ^4.3.6 は globalThis.animejs として公開されています | — |
| GSAP (GreenSock) | gsap | CSS や CanvasAnimation では実現できない、協調的な UI/キャンバスアニメーション | references/canvas-and-pixi.md の「GSAP」セクション |
| jQuery | $, jQuery | アプリ v1 と旧式の renderChatMessage フック用に引き続きバンドルされています(^3.7.1)。旧式のため、ネイティブ DOM API を使用してください | references/migration-guide.md |
適している場合はこれらを使用し、対応できない場合にのみ npm 依存関係を導入してください。
開発ツールとエコシステム
公式 CLI(@foundryvtt/foundryvtt-cli)
npm install -g @foundryvtt/foundryvtt-cli
公式 CLI(fvtt コマンド)は、コンペンディウムの管理に使用します。LevelDB パックを個別の JSON/YAML ファイルに展開し、再パッケージ化できます。コンペンディウムのコンテンツをバージョン管理するうえで不可欠です。
fvtt package workon my-module # set active package context
fvtt package extract --type Module # extract compendium to JSON files
fvtt package pack --type Module # repackage JSON back to LevelDB
TypeScript(@league-of-foundry-developers/foundry-vtt-types)
Foundry VTT API 全体を対象とした、コミュニティ保守の型定義です。型付けされた game、CONFIG、Hooks、およびすべてのコアクラスを提供します。
npm add -D fvtt-types@github:League-of-Foundry-Developers/foundry-vtt-types#main
// tsconfig.json
{
"compilerOptions": {
"types": ["fvtt-types"],
"target": "esnext",
"moduleResolution": "bundler",
"strict": true
}
}
型定義を利用する前に、リポジトリで v14 対応状況を確認してください。v14 のスキーマ変更(ActiveEffect の system.changes、Scene Levels、Regions の構造、messageMode)への対応が、コアに追いついていない可能性があります。型定義がない場合は、型アサーションを使って先に進んでください。
ランタイム:Node 24
v14 サーバーには Node >=24.13.1 <25.0.0 が必要です(v13 では Node 20~22 が必要で、両者に互換性はありません)。ローカル開発、CI、Foundry ホストの環境を一致させるため、ビルドツールにも同じ Node を使用してください。
ビルドツール(Vite/Rollup)
Foundry モジュールは標準的なウェブアプリです。TypeScript のコンパイル、SCSS、npm 依存関係、バンドルには、Vite(推奨)または Rollup を使用してください。ソースは src/ に配置し、出力先は dist/ とします。dist/ は Foundry の Data/modules/<id>/ にシンボリックリンクします。
Foundry 固有の制約は 1 つだけです。アセットのハッシュ化は使用できません。Foundry は module.json に指定された固定パスでファイルを参照するため、出力ファイル名に [hash] を含めるとマニフェストが機能しなくなります。JS、CSS、コピーしたアセットのハッシュ化を無効にしてください。
boilerplate/ には、Foundry 向けに設定済みですぐ使える vite.config.mjs、rollup.config.mjs、package.json が含まれています。開発用のポート 30000 へのプロキシ、固定名での出力、テンプレート/言語ファイル/マニフェスト用のコピープラグイン、ソースマップ、ウォッチモードが設定されています。
(プロジェクト構成、開発ループ、flags.hotReload との連携、本番ビルド、ビルド前のマニフェスト注入、注意点を含む)完全なガイドについては、references/build-pipeline.md を参照してください。
リアクティブフレームワーク(Svelte/Lit)
ApplicationV2 では、Handlebars の代わりにリアクティブフレームワークを簡単にマウントできます。複雑なモジュール UI では、仮想 DOM によるオーバーヘッドがないため、コミュニティでは Svelte が好まれています。_renderHTML() をオーバーライドしてフレームワークをマウントし、_onClose() をオーバーライドして破棄してください。
モジュールテンプレート
League of Foundry Developers は、Vite、TypeScript、ESM をあらかじめ設定したスターターテンプレートを管理しています。GitHub で League-of-Foundry-Developers/FoundryVTT-Module-Template を参照してください。
Foundry への公開
https://foundryvtt.com/packages/submit からモジュールを提出してください。GitHub Releases を使用する場合、マニフェスト URL とダウンロード URL は次のパターンに従う必要があります。
manifest: https://github.com/you/my-module/releases/latest/download/module.json
download: https://github.com/you/my-module/releases/download/v1.0.0/module.zip
manifest URL は常に latest を指し、Foundry が更新を自動検出できるようにします。download URL はバージョン指定で、Foundry はこれを使用して特定のリリースをインストールします。
リリースを自動化するための最小限の GitHub Actions ワークフロー:
name: Release
on:
push:
tags: ["v*"]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: zip -r module.zip module.json scripts/ templates/ styles/ lang/ packs/
- uses: softprops/action-gh-release@v2
with:
files: module.zip
generate_release_notes: true
git tag v1.0.0 && git push --tags でリリースにタグを付けると、ワークフローが実行されます。タグを付ける前に module.json のバージョンを更新してください。
Tours API
UI 要素を強調表示し、機能を順を追って案内する、対話型のガイド付きツアーです。ツアーは JSON ファイルで定義し、game.tours.register() を介して登録します。
modules/my-module/tours/welcome.json にツアー JSON ファイルを作成します。
{
"title": "MY_MODULE.Tour.welcome.title",
"description": "MY_MODULE.Tour.welcome.description",
"canBeResumed": false,
"display": true,
"steps": [
{
"id": "step1",
"title": "MY_MODULE.Tour.welcome.step1",
"content": "MY_MODULE.Tour.welcome.step1Content",
"selector": ".my-module-panel .header"
},
{
"id": "step2",
"title": "MY_MODULE.Tour.welcome.step2",
"content": "MY_MODULE.Tour.welcome.step2Content",
"selector": ".my-module-panel button[data-action='roll']"
},
{
"id": "step3",
"title": "MY_MODULE.Tour.welcome.step3",
"content": "MY_MODULE.Tour.welcome.step3Content",
"selector": ".my-module-panel .inventory"
}
]
}
setup フックで登録します。
Hooks.once("setup", async () => {
game.tours.register(
"my-module",
"welcome",
await foundry.nue.Tour.fromJSON("/modules/my-module/tours/welcome.json")
);
});
// Start programmatically (e.g., on first module load)
Hooks.once("ready", async () => {
if (!game.settings.get("my-module", "tourCompleted")) {
await game.tours.get("my-module.welcome").start();
await game.settings.set("my-module", "tourCompleted", true);
}
});
各ステップでは、selector を介して DOM 要素を強調表示します。sidebarTab フィールド(任意)を指定すると、ステップの前にサイドバーのタブが自動的に切り替わります。ステップでは、ツールチップの配置を指定する tooltipDirection(UP、DOWN、LEFT、RIGHT)を定義できます。
テスト(@ethaks/fvtt-quench)
Foundry 環境内で実行される、Mocha/Chai を使用したゲーム内テストフレームワークです。Foundry の API には初期化済みのゲーム状態が必要なため、テストにはこのフレームワークが必要です。quenchReady フックでテストバッチを登録します。Quench が有効な場合にのみテストファイルを遅延インポートし、本番バンドルが肥大化しないようにします。バッチ登録のパターン、ドキュメント/シート/フック/ロールのテストレシピ、Quench(手動)と純粋なヘルパーの単体テスト(GitHub Actions の Vitest)を分ける CI 戦略については、references/testing-with-quench.md を参照してください。
コミュニティライブラリ
| ライブラリ | 用途 | 使用する場面 |
|---|---|---|
| libWrapper | Foundry コアメソッドを安全にモンキーパッチする | コアの動作を変更するとき(例:Token.prototype.draw)。モジュール間の競合を防ぎます |
| socketlib | クライアント間の通信を簡素化する | 生の game.socket を使うより簡単な方法です。プレイヤークライアントから GM の応答を await できます |
| Developer Mode | モジュールごとに統一されたデバッグフラグを設定する | UI からモジュールごとに切り替えられる構造化ログ |
モジュール API パターン
他のモジュールが連携できるように、公開 API を提供します。
Hooks.once("init", () => {
game.modules.get("my-module").api = {
getHeroData: (actorId) => game.actors.get(actorId)?.system,
rollAbility: async (actorId, ability) => { /* ... */ },
};
});
他のモジュールからは、game.modules.get("my-module")?.api?.getHeroData(id) を介してアクセスできます。
デバッグ
- ブラウザーの DevTools(F12) — 主なデバッグツール
CONFIG.debug.hooks = true— すべてのフック呼び出しをコンソールに記録しますui.notifications.info/warn/error()— テスト用のゲーム内フィードバック- Developer Mode モジュール — モジュールごとのデバッグフラグの切り替え
アクティブエフェクト
ActiveEffect は、ドキュメントデータを一時的に変更するための Foundry のシステムです。バフ、デバフ、状態、ステータス効果などが該当します。Actor と Item に埋め込みドキュメントとして保存されます。
v14 での変更 — ActiveEffect V2。 ActiveEffect は型付きドキュメントです。変更内容はルートの changes 配列から system.changes に移され、foundry.data.ActiveEffectTypeDataModel で定義されます。各変更内容は { key, type, value, phase, priority } です。
await actor.createEmbeddedDocuments("ActiveEffect", [{
name: "Blessed",
img: "icons/svg/angel.svg",
type: "base",
system: {
changes: [{ key: "system.abilities.str", type: "add", value: 2, phase: "initial" }]
},
duration: { value: 10, units: "rounds", expiry: "turnStart" }
}]);
// Toggle an effect
const effect = Array.from(actor.allApplicableEffects()).find(e => e.name === "Blessed");
await effect.update({ disabled: !effect.disabled });
| フィールド | v13 | v14 |
|---|---|---|
| 変更内容 | ルートの changes: [...] | system.changes: [...] |
| 変更操作 | 数値の mode(CONST.ACTIVE_EFFECT_MODES) | 文字列の type(CONST.ACTIVE_EFFECT_CHANGE_TYPES) |
| アイコン | icon | img |
| 持続時間 | { rounds, turns, seconds, startTime, startRound } | { value, units, expiry, expired } と、別個の start スキーマ |
| Origin | StringField | DocumentUUIDField({ relative: true }) |
| アイコン表示 | overlay ブール値 | showIcon(CONST.ACTIVE_EFFECT_SHOW_ICON.NEVER | CONDITIONAL | ALWAYS) |
migrateData は読み込み時に古いドキュメントを変換するため、v13 のデータも引き続き使用できます。ただし、新しい形式で書き込んでください。
変更タイプ(CONST.ACTIVE_EFFECT_CHANGE_TYPES。既定の優先度とともに記載):custom(0)、multiply(10)、add(20)、subtract(20)、downgrade(30)、upgrade(40)、override(50)。add と subtract は文字列の連結や、配列および Set への追加・削除にも対応します。パッケージは CONFIG.ActiveEffect.changeTypes({ label, defaultPriority, handler, render })に独自のタイプを登録でき、任意の type 文字列も使用できます。認識されないタイプは無視されます。
フェーズ。 CONST.ACTIVE_EFFECT_CHANGE_PHASES は ["initial", "final"] です。コアは派生データの前に actor.applyActiveEffects("initial") を、後に applyActiveEffects("final") を呼び出します。追加のフェーズは CONFIG.ActiveEffect.phases に登録し、自分で applyActiveEffects("myPhase") を呼び出してください。それ以外の処理は行われません。
持続時間と期限切れ。 duration.units は CONST.ACTIVE_EFFECT_DURATION_UNITS(years、months、days、hours、minutes、seconds、rounds、turns)のいずれかです。duration.expiry は CONST.ACTIVE_EFFECT_EXPIRY_EVENTS(combatStart、roundStart、turnStart、combatEnd、roundEnd、turnEnd)のいずれか、または CONFIG.ActiveEffect.expiryEvents に追加したキーです。ActiveEffect.registry は期限切れ待ちの効果を追跡します。CONFIG.ActiveEffect.expiryAction(既定値は "update")によって、期限切れ時に duration.expired を設定するか、効果を削除するかが決まります。カスタムの期限切れイベントは ActiveEffect.registry.refresh() を通じて発火します。
変更タイプの適用方法を変えるには、ActiveEffect サブクラスの静的メソッド _applyChangeUnguided、_applyChangeAdd、_applyChangeSubtract、_applyChangeMultiply、_applyChangeOverride、_applyChangeUpgrade、_applyChangeCustom をオーバーライドしてください。古いインスタンスメソッド(apply、_applyAdd など)は v16 まで非推奨です。
CONFIG.ActiveEffect.legacyTransferral は廃止されました。アイテムの効果は常にアイテム上に保存され、allApplicableEffects() を通じて読み取られます。
サブタイプ、レジストリ、コンペンディウムに保存された効果、変更処理パイプライン全体については、references/active-effects-v2.md を参照してください。
グローバルステータス効果
CONFIG.statusEffects はステータス ID をキーとする Proxy です。ID を指定して割り当てます。
Hooks.once("init", () => {
CONFIG.statusEffects["my-module.burning"] = {
id: "my-module.burning",
name: "MY_MODULE.Effect.burning",
img: "modules/my-module/icons/burning.svg",
system: {
changes: [{ key: "system.abilities.dex", type: "subtract", value: 2 }]
}
};
});
push() も引き続き使用できますが、ID を指定して割り当ててください。既存の項目が適切に置き換えられます。パレット全体を置き換えるシステムでは、引き続き配列を割り当てることもできます。
すべての効果を取得する
actor.effects に含まれるのは、Actor に直接設定された効果のみです。Item から転送された効果を取得するには、allApplicableEffects() が必要です。
for (const effect of actor.allApplicableEffects()) {
console.log(effect.name, effect.disabled, effect.isTemporary);
}
// Categorize for sheet display
function prepareActiveEffectCategories(effects) {
const categories = {
temporary: { label: "Temporary", effects: [] },
passive: { label: "Passive", effects: [] },
inactive: { label: "Inactive", effects: [] }
};
for (const e of effects) {
if (e.disabled) categories.inactive.effects.push(e);
else if (e.isTemporary) categories.temporary.effects.push(e);
else categories.passive.effects.push(e);
}
return categories;
}
// Usage in _prepareContext
context.effects = prepareActiveEffectCategories(actor.allApplicableEffects());
主なプロパティ:e.disabled(非アクティブ)、e.isTemporary(duration.expiry が設定されている場合、または duration.value が有限の場合に true。v14 で対象が拡大されました)、e.showIcon(トークン上のアイコン表示)。
アドベンチャードキュメント
Adventure は、キャンペーンモジュール、ワンショット、エンカウントパックなどの既製コンテンツを配布するための、Foundry 公式のドキュメントタイプです。1つのアドベンチャーに、シーン、アクター、アイテム、ジャーナル、テーブル、マクロ、プレイリスト、カード、およびそれらを整理するフォルダー階層をまとめられます。ユーザーはすべてをワンクリックでインポートできます。
// Programmatic import (selective)
const pack = game.packs.get("my-module.starter-adventure");
const adventure = await pack.getDocument("adventureDocId");
// Preview before committing
const data = await adventure.prepareImport();
// Import — all collections by default, opt out per collection
await adventure.import({ scenes: false });
module.json で type: "Adventure" としてパックを宣言します。ワールド内でコンテンツを作成し、fvtt package pack でビルドして、packs/ に格納します。再インポート時に正しく更新されるよう、すべての場所で安定した ID を使用してください。
作成の流れ、プログラムからのインポート、バージョン更新、preImportAdventure フック、注意点(ID の重複、シーンのサムネイル、マクロの権限、プレイリストのパス)については、references/adventure-documents.md をお読みください。
権限と所有権
Foundry の権限モデルには、独立した2つの要素があります。ユーザーのロール(ゲーム全体の権限。USER_ROLES.PLAYER から GAMEMASTER まで)と、ドキュメントの所有権(ドキュメントごとのアクセス権。OWNERSHIP_LEVELS.NONE から OWNER まで)です。変更が許可されるのは、両方のチェックに合格した場合のみです。
// UI-level check (gate a button)
if (actor.canUserModify(game.user, "update")) { /* show edit UI */ }
if (actor.testUserPermission(game.user, "OBSERVER")) { /* show sheet */ }
// Granular permission (GM-configurable thresholds per role)
if (game.user.hasPermission("ACTOR_CREATE")) { /* show "New" button */ }
// Set ownership programmatically
await actor.update({ "ownership.default": 2, [`ownership.${player.id}`]: 3 });
埋め込みドキュメントのデフォルトは ownership: { default: -1 }(親から継承)です。プレイヤーは、自分が所有していないワールドドキュメントを変更できません。プレイヤーが開始した変更には、GM が権限を持つソケットパターンを使用してください。クライアント側のチェックは UX のためのものであり、サーバー側でも独立して検証されます。
ロールの階層、詳細な USER_PERMISSIONS、所有権の継承、GM が権限を持つパターン、コンペンディウムパックの所有権、エラーが通知されずに失敗する場合の注意点については、references/permissions-and-ownership.md をお読みください。
移行とメンテナンス
Foundry のバージョンをまたいでモジュールを更新する場合や、データスキーマを変更する場合は、ワールドの読み込み時に移行を実行してください。
const MIGRATIONS = [
{ version: 1, fn: migrateV1 },
{ version: 2, fn: migrateV2 },
];
Hooks.once("ready", async () => {
if (!game.user.isGM) return;
const current = game.settings.get("my-module", "schemaVersion") ?? 0;
const target = MIGRATIONS[MIGRATIONS.length - 1].version;
if (current >= target) return;
for (const { version, fn } of MIGRATIONS) {
if (current < version) await fn();
}
await game.settings.set("my-module", "schemaVersion", target);
ui.notifications.info("my-module | Migration complete.");
});
v13 モジュールを v14 に移行しますか? まず references/v14-migration.md をご確認ください。ここには、すべての破壊的変更、置き換え方法と削除バージョンを含む各非推奨項目、およびチェックリストが記載されています。主な変更点は、ActiveEffect の変更が文字列型を使用する system.changes に移動したこと、MeasuredTemplate が Regions になったこと、Scene の背景/前景が Level ドキュメントに移動したこと、rollMode が messageMode になったこと、-=/== 更新キーが _del/_replace になったこと、そして v12 のグローバルシムが削除されたことです。
以前のバージョン間の移行(Application から ApplicationV2 への移行、同期ロールの削除)、データ移行のパターン、互換性フラグ、migrateWorld を使用した一括移行、移行レジストリーの全体的なパターンについては、references/migration-guide.md をお読みください。
ボイラープレートファイル
新しいモジュールを作成する際の出発点として、以下をコピーしてください。
| ファイル | 用途 |
|---|---|
boilerplate/module.json | 有効な v14 マニフェスト — compatibility 14、styles オブジェクト、documentTypes |
boilerplate/main.mjs | init/setup/ready、設定、シート登録を含む ES モジュールのエントリーポイント |
boilerplate/type-data-model.mjs | TypeDataModel と defineSchema を使用したカスタム Actor タイプ |
boilerplate/actor-sheet.mjs | ActorSheetV2 + HandlebarsApplicationMixin |
boilerplate/sheet.hbs | キャラクター情報、能力、所持品を含む Handlebars テンプレート |
boilerplate/socket-handler.mjs | GM が権限を持つソケットパターン |
boilerplate/vite.config.mjs | Vite の設定 — 開発用プロキシ、固定名の出力、静的ファイルコピー用プラグイン |
boilerplate/rollup.config.mjs | Rollup の代替設定 — node-resolve、commonjs、postcss、copy、terser |
boilerplate/package.json | スクリプト(dev、build、link)、devDependencies、Node 24 の engines 固定 |
よくある落とし穴
- 非推奨の v1 クラスを使用する —
Application、FormApplication、ActorSheet、ItemSheetはfoundry.appv1の下にあり、v16 で削除されます。ApplicationV2、DocumentSheetV2を使用してください。 - ロールを await し忘れる —
roll.roll()はevaluate()の非同期エイリアスにすぎません。必ずawait roll.evaluate()を使用してください。Roll.evaluateSync()は決定的なロール(maximize/minimize)でのみ使用できます。 - ソケットのプレフィックスがない — ソケットイベントには
"module.my-module"形式を使用する必要があります。module.プレフィックスがないと、メッセージはルーティングされません。 - init で game.user にアクセスする —
init中はgame.userが設定されていません。ユーザーに依存するロジックにはreadyフックを使用してください。 - GM 以外がワールドドキュメントを変更する — ワールドレベルのドキュメントを変更できるのは GM クライアントだけです。更新は成功したように返されますが、変更は保存されません。試行する前に
document.canUserModify(user, "update")を使用し、プレイヤーが開始した変更は GM が権限を持つソケットパターンを通じて処理してください。references/permissions-and-ownership.mdを参照してください。 esmodulesの代わりにscriptsを使用する —scriptsフィールドはファイルを従来のスクリプトとして読み込みます(モジュールスコープはありません)。適切な ES モジュールのサポートには、必ずesmodulesを使用してください。独立したworldScriptsフィールドはありません。この概念はコミュニティで誤って広まった呼び名です。すべてのモジュールスクリプトは、マニフェストのesmodules/scriptsを介してクライアント側で実行されます。フック(init、setup、ready)と条件チェックを使って、コードを実行するタイミングと場所を制御してください。- 内部設定で
config: falseを指定し忘れる —config: trueの設定は、モジュール設定メニューに表示されます。スキーマのバージョンなど、プログラムからのみ使用する値にはconfig: falseを使用してください。 - 英語文字列をハードコードする — ユーザーに表示されるテキストには、英語のみをサポートする場合でも
game.i18n.localize()を使用してください。将来のローカライズが簡単になります。 - フックのクリーンアップをしない —
Hooks.on()のフック ID を保存し、アプリケーションを閉じるときにHooks.off()を呼び出してください。フックのリークはメモリの問題や動作の重複を引き起こします。 compatibility.maximumを厳しく設定しすぎる — ユーザーが新しい Foundry バージョンでモジュールを実行できなくなります。互換性を損なう問題を確認した場合にのみ設定してください。- ライフサイクルフックで、すべてのクライアント上で副作用を実行する —
_onCreate、_onUpdate、_onDeleteは接続中のすべてのクライアントで発火します。if (game.userId !== userId) return;でガードし、発生元のクライアントでのみ副作用を実行してください。 - libWrapper を使わずにコアメソッドをパッチする — 直接モンキーパッチを行うと、複数のモジュールが同じメソッドを変更した際に問題が発生します。Foundry のコア関数を安全に、競合なくパッチするには
libWrapperを使用してください。 - カスタム Actor タイプに
getRollData()がない —getRollData()を実装しないと、@abilities.strのようなロール式が解決されません。ロールに必要なシステムデータを返してください。 - jQuery を使用する — jQuery は appv1 と従来の
renderChatMessageフック用に引き続きバンドルされていますが、フックと_onRenderにはネイティブのHTMLElementが渡されます。$()の代わりにquerySelector、addEventListener、classListを使用してください。 @layerを使わない CSS、または独自に作ったレイヤーの CSS — Foundry は CSS カスケードレイヤーを使用します。マニフェストでmodulesを指定してください:styles: [{ "src": "...", "layer": "modules" }]。コアが宣言していないレイヤー名はexceptionsの後に並び、すべてのコア CSS を上書きします。rollModeを渡す(v14) —toMessage、ChatMessage.create、RollTable#draw、rollInitiativeにおけるrollModeは非推奨です。messageMode(public | gm | blind | self | ic)を渡すか、何も渡さないでください。toMessageは自動的にcore.messageMode設定を使用します。- 数値の ActiveEffect 変更モード(v14) —
CONST.ACTIVE_EFFECT_MODESとルートのchanges配列は非推奨です。文字列のtype値(add、subtract、multiply、override、upgrade、downgrade、custom)を使ってsystem.changesを記述してください。 -=と==の更新キー(v14) — v16 まで非推奨です。foundry.data.operatorsの_delと_replace(value)、またはmergeObject(..., { applyOperators: true })を使用してください。- MeasuredTemplates の作成(v14) —
MeasuredTemplateDocument、TemplateLayer、Scene#templatesは非推奨です。代わりにcanvas.regions.placeRegion({ shapes: [...] })で Region を配置してください。references/measured-templates.mdを参照してください。 scene.backgroundの読み取り(v14) — 背景、前景、およびそれらの高さはLevelドキュメントに移動しました。canvas.level/scene.levelsを読み取ってください。Scene のゲッターは非推奨の互換用シムです。references/scene-levels.mdを参照してください。CONFIG.<Doc>.layerClassの設定(v14) — これは非推奨のプロキシです。代わりにCONFIG.Canvas.layers.<name>.layerClassを割り当ててください。- バンドルされた
.htmlファイルをブラウザーで開く(14.361 以降) — 静的な.htmlはtext/plainとして配信されます。Handlebars テンプレートは引き続き Foundry 経由でレンダリングされます。直接ブラウザーで開く場合のみ動作しなくなりました。 template.jsonを同梱する(v14) — v16 まで非推奨です。マニフェストのdocumentTypesとTypeDataModelで型を宣言してください。game.templateは削除されました。game.modelを読み取ってください。
リファレンスファイル
API の詳細については、必要に応じて読み込まれる以下のファイルを参照してください。
| ファイル | 読むタイミング |
|---|---|
references/v14-migration.md | モジュールを v13 から v14 に移行する場合は、まずここを読んでください — 破壊的変更、削除予定バージョンを含む非推奨項目、チェックリスト |
references/document-model.md | カスタム Actor/Item タイプ、TypeDataModel、defineSchema、フラグ、ライフサイクルフック、Journal ページ、Folder API の構築 |
references/application-v2.md | シート、ウィンドウ、ダイアログの構築 — ApplicationV2、DocumentSheetV2、HandlebarsApplicationMixin、ドラッグ&ドロップ |
references/hooks-and-settings.md | フックのライフサイクル、ドキュメントフック、キャンバスフック、設定 API、サブメニュー、キーバインド、DataModel 設定 |
references/chat-and-ui.md | チャットコマンド、メッセージのレンダリング、コンテキストメニュー、enrichHTML、FilePicker、セキュリティ/認可、ProseMirror エディター |
references/sockets-rolls-packs.md | ソケット通信、ダイス/Roll 拡張、コンペンディウムパック、ローカライズ |
references/canvas-and-pixi.md | カスタムキャンバスレイヤー、PlaceableObject、PIXI.js の統合、座標変換 |
references/combat-and-tokens.md | 戦闘トラッカー、イニシアチブ、Token HUD、シーンコントロール、プロトタイプトークンの設定 |
references/regions-and-grid.md | シーンリージョン API、RegionDocument、RegionBehavior、グリッドの計測、座標変換、グリッドのハイライト表示 |
references/scene-levels.md | レベルドキュメント、canvas.level、レベルごとの背景とフォグ、レベル対応の配置オブジェクト |
references/active-effects-v2.md | ActiveEffect V2 — system.changes、変更タイプとフェーズ、持続時間と期限切れ、レジストリ、効果のサブタイプ |
references/vision-and-lighting.md | VisionMode、検出モード、ライティングシステム、AmbientLight、戦場の霧 |
references/measured-templates.md | リージョンを使ったテンプレート — 効果範囲の形状、placeRegion、トークンの放射、MeasuredTemplate からの移行 |
references/audio-and-macros.md | AudioHelper、プレイリスト、効果音、Macro の作成、ホットバーとの統合 |
references/module-subtypes.md | モジュールが提供する Actor/Item/JournalEntryPage サブタイプ、htmlFields/filePathFields、無効化時の動作 |
references/accessibility.md | ARIA ロール、キーボードナビゲーション、フォーカス管理、色のコントラスト、prefers-reduced-motion |
references/testing-with-quench.md | Quench(Mocha/Chai)を使ったゲーム内テストの作成、テストパターン、CI 戦略 |
references/build-pipeline.md | Vite と Rollup の設定、開発用プロキシ、ホットリロード、アセットパス、ビルド前のマニフェスト注入 |
references/handlebars-and-templates.md | Foundry ヘルパーの一覧、カスタム HTML 要素、フォームヘルパー、カスタムヘルパー/パーシャル、テンプレートの事前読み込み、シートのパターン |
references/adventure-documents.md | Adventure ドキュメント、シーン/アクター/アイテム/ジャーナルのパッキング、プログラムによるインポート、バージョン更新 |
references/permissions-and-ownership.md | ユーザーロールとドキュメントの所有権、testUserPermission、canUserModify、USER_PERMISSIONS、GM 権限を基準とするパターン |
references/migration-guide.md | 古いバージョンからの移行(v11→v12→v13)、非推奨 API の検出、データ移行スクリプト |