When: A player takes a hit, you're adjudicating damage, or running a script effect.
Find the actor ID if you don't have it:
search_documents(query: "Character Name", documentTypes: ["Actor"])
Apply damage via run_javascript:
const actor = game.actors.get("ACTOR_ID");
return await actor.applyDamage(AMOUNT, "TYPE", "tempHp");
{ original, actual } — actual may be lower due to resistances.If you want to preview first without applying:
return game.actors.get("ACTOR_ID").system.calculateDamage(AMOUNT, "TYPE", "tempHp");
run_javascript:const actor = game.actors.get("ACTOR_ID");
await actor.applyRestore("health", { useSurge: true, consumeSurge: true });
When: You need to set a specific value without surge logic (e.g., "set HP to 5", "set sanity to 0").
Read the actor first (required for update):
read_document(documentType: "Actor", documentId: "ACTOR_ID")
Update the specific field:
update_document(
documentType: "Actor",
documentId: "ACTOR_ID",
updates: { "system.health.value": 5 }
)
run_javascript with the tracker summary from common-scripts.read_document(documentType: "Combat", documentId: "COMBAT_ID") and look at system.bms.downs.To find the active combat ID: run_javascript → return game.combat?.id
When: You need to add an action to the tracker on behalf of a combatant.
Find the actor and item:
const actor = game.actors.getName("CHARACTER NAME");
const item = actor.items.getName("ACTION NAME");
return { actorId: actor.id, itemId: item.id, swings: item.system.swings };
Queue it:
await game.combat.queueActionFromSheet(
game.actors.get("ACTOR_ID"),
"ACTION_ITEM_ID",
0 // swing index (0 = first swing)
);
When: Casting a spell, applying a condition, or tracking something that expires after N downs.
const combat = game.combat;
const durationDowns = 4; // 4 downs = 2 ticks = 2 seconds
await combat.addEffectToDown(durationDowns, {
name: "Blinded",
description: "Cannot see",
icon: "icons/svg/blind.svg",
combatantId: "COMBATANT_ID", // or null for unlinked
activeEffectId: null, // or a real ActiveEffect ID to auto-disable on expiry
resolutionTime: durationDowns,
originalResolutionTime: durationDowns,
});
Duration reference: 2 downs = 1 tick (1 second), 12 downs = 1 round (6 seconds).
When: A spell or ability grants a status effect.
Option A — Enable an existing (disabled) effect:
const actor = game.actors.get("ACTOR_ID");
const ae = actor.effects.getName("EFFECT NAME");
await ae.update({ disabled: false });
Option B — Create a new effect:
read_document the actor first, then:update_document(
documentType: "Actor",
documentId: "ACTOR_ID",
operations: [{
type: "insert",
collection: "effects",
data: {
name: "Burning",
img: "icons/svg/fire.svg",
disabled: false,
changes: [{ key: "system.resistances.fire", mode: 2, value: "0.5" }]
}
}]
)
run_javascript to inspect an existing NPC for reference schema:
const npc = game.actors.find(a => a.type === "npc");
return npc ? { id: npc.id, name: npc.name } : "No NPCs found";
If you need to see the full schema:
read_document(documentType: "Actor", documentId: "EXISTING_NPC_ID")
Create:
create_document with documentType: "Actor" and include type: "npc" in the data.system.*.create_document calls.Check for lingering tracker effects with linked ActiveEffects:
const c = game.combat;
return c.system.bms.downs.flatMap(d => d.effects.filter(e => e.activeEffectId))
.map(e => ({ name: e.name, actor: e.actor?.name }));
If any linked effects exist, confirm with the GM whether to disable them.
End combat:
await game.combat.endCombat();
(This will prompt the GM in-client to disable linked effects.)
When: Creating a consumable or activatable item that fires a BMS trigger when used.
IMPORTANT RULES — read before attempting:
[] or use always — NEVER use isThisItem (it doesn't exist and silently blocks the trigger){actor.name} tokens, NOT @actoramount in outcomes must be a string (e.g. "1" not 1)system.triggers.0.hook — always update via the array fieldlist_documents(documentType: "Item")
read_document(documentType: "Item", documentId: "EXISTING_ITEM_ID")
create_document callInclude ALL trigger data inline. No follow-up patching needed.
{
"documentType": "Item",
"data": {
"name": "Surge Restorer",
"type": "item",
"img": "icons/svg/item-bag.svg",
"system": {
"description": "Restores 1 surge when used.",
"quantity": 1,
"usable": true,
"triggers": [
{
"id": "",
"hook": "bms.itemUsed",
"enabled": true,
"conditions": [],
"outcomes": [
{
"id": "",
"type": "changeResource",
"config": { "resource": "surges", "operation": "add", "amount": "1" }
},
{
"id": "",
"type": "chatMessage",
"config": { "content": "{actor.name} restores 1 surge." }
}
]
}
]
}
}
}
read_document(documentType: "Item", documentId: "CREATED_ITEM_ID")
Confirm triggers[0].hook is correct and outcomes contains the right entries.
system.triggers supports partial updates — include only the fields you want to change, identified by id. Nested conditions and outcomes are also merged by id.
Change just a trigger's hook (all other fields preserved):
{ "updates": { "system.triggers": [{ "id": "triggerId", "hook": "bms.combatDown" }] } }
Update one outcome's config (other outcomes preserved):
{
"updates": {
"system.triggers": [{
"id": "triggerId",
"outcomes": [{ "id": "outcomeId", "config": { "content": "New message." } }]
}]
}
}
Full replacement (omit id from all items — same behaviour as before):
{ "updates": { "system.triggers": [{ "hook": "bms.itemUsed", "enabled": true, "conditions": [], "outcomes": [...] }] } }
If you need to read_document first to get the current trigger IDs, do so — but you only need to send back the fields you're changing.
Search journal entries:
search_documents(query: "keyword", documentTypes: ["JournalEntry"])
Read the result:
read_document(documentType: "JournalEntry", documentId: "JOURNAL_ID")
Then look through pages for the relevant page content.
const actor = game.actors.get("ACTOR_ID");
return {
id: actor.id,
name: actor.name,
type: actor.type,
items: actor.items.map(i => ({ id: i.id, name: i.name, type: i.type })),
effects: actor.effects.contents.map(e => ({ id: e.id, name: e.name, disabled: e.disabled })),
resources: Object.keys(actor.system.resources),
actionGroups: actor.system.actionGroups?.map(g => g.label) ?? [],
};