SC - More Activities
SC - More Activities
Overview
SC - More Activities extends the native D&D 5e activity system in
Foundry VTT. It does not replace the standard dnd5e activity workflow. It
adds new activity types, a Shattered Codex-owned registration layer, a grouped
activity creation dialog, GM-facing diagnostics, and explicit migration tools
for worlds moving away from the legacy more-activities module.
The module is built for two different jobs:
- GMs who want more activity types without maintaining custom patches
- module authors who want to register custom
dnd5eactivity types through a stable public API
At release, the module ships with 11 built-in SC activity types and a registry flow that can also expose third-party activity types in the same creation UI.
Installation
- In Foundry VTT, open Add-on Modules.
- Choose Install Module.
- Paste this manifest URL:
https://github.com/Shattered-Codex/sc-more-activities/releases/latest/download/module.json- Install the module.
- Enable SC - More Activities in your world.
- Install and enable
libWrapperif you want the recommended compatibility layer.
Compatibility:
- Foundry VTT
v13tov14 - system:
dnd5e dnd5ecompatibility declared inmodule.json: minimum5.0.0, verified5.3.0- localization: English and Brazilian Portuguese
Important behavior:
- the module exits early outside
dnd5eworlds - native
dnd5eactivities remain available - the module relies on items and sheets that already support the
dnd5eactivity workflow
What It Adds
- 11 built-in Shattered Codex activity types for automation, support, progression, inventory, and canvas workflows
- a public registration hook and API for Shattered Codex modules and third-party modules
- a grouped activity creation dialog that separates D&D 5e, Shattered Codex, and External activity groups
- a GM Activity Catalog with diagnostics, filters, and enable or disable controls
- explicit migration tools for legacy
more-activitiesactivity data - per-user preview color settings for teleport, movement, and wall overlays
- module settings shortcuts for the wiki, support links, catalog, migration tools, and preview colors
Built-In Activity Types
| Activity | Use it for | Main configuration | Important notes |
|---|---|---|---|
| SC Sound | Play an audio file after the activity is used. | Audio file, Playback, and Volume. | Broadcasting to Everyone requires a GM. Non-GM use falls back to local playback. |
| SC Macro | Run a selected world macro or inline code stored on the activity. | Macro source, World macro, and Inline code. | Inline code runs only for GMs. World macros still respect the user's macro permissions. |
| SC Hook | Emit a hook or call a whitelisted callback exposed by another module. | Dispatch mode, Hook name, Callback module, and Callback id. | This is primarily an integration and developer workflow, not a player-facing automation shortcut. |
| SC Chain | Execute other activities from the same item in order. | Activity ids, Depth limit, Continue after failure, and Stop when canceled. | Includes loop detection and nested-depth protection. |
| SC Conditional Chain | Branch between activities on the same item based on results, actor data, rolls, or manual choices. | First step, per-step activity, condition type, routes, and flow policies. | Steps can also be decision-only. The flow keeps using the most recent child activity result until another activity runs. |
| SC Contest | Resolve a contested roll between the activity user and a defender. | Defender, Tie rule, and roll configuration for both sides. | The defender can be the selected target or the same actor. Custom formulas are supported. |
| SC Grant | Grant configured items to the activity actor or to a targeted actor. | Recipient, granted item UUIDs, and Quantity. | Using the target flow requires exactly one target token with a valid actor. |
| SC Advancement | Trigger selected dnd5e advancement entries from a source item. | Source item and selected Advancement Entries. | The actor must own the source item at use time. |
| SC Teleport | Choose teleport targets and place their destination on the canvas. | Targets, Maximum targets, Allow self, Target range, Teleport range, destination source, offsets, and arrangement controls. | Scene movement is GM-mediated. Destination placement can be limited by range. |
| SC Movement | Push or pull tokens from the activity actor with a preview workflow. | Targets, Maximum targets, Snap to grid, Movement type, Distance, and Range limit. | Scene movement is GM-mediated and respects the configured range limit. |
| SC Wall | Create walls by placing points directly on the canvas. | Wall type, Placement range, wall limits, panel settings, facing, and block properties. | Can be restricted to GM-only creation or allow player requests through an active GM. |
The built-in activities are registered under the Shattered Codex group in the activity creation dialog, but the catalog still tracks their category and source metadata separately.
SC Conditional Chain
SC Conditional Chain is the branching version of SC Chain.
Use it when one activity should lead to different outcomes depending on what just happened. A simple example is:
- roll an attack
- deal damage only if the attack hits
- trigger a bonus effect only if the damage total is high enough
Important scope:
- a conditional chain can call only activities that already exist on the same item
- every step can either run one activity or be Decision only (no activity)
- decision-only steps keep using the most recent child activity result until a later step runs another activity
Before You Build The Flow
Build the child activities first. Then add SC Conditional Chain to the same item and use it as the controller.
This is the easiest order for non-technical setup:
- Create the attack, damage, healing, grant, macro, or other activities you want the item to use.
- Add SC Conditional Chain.
- Open the chain's Effect tab.
- Add your steps in the order you want to think about them.
- Pick the First step.
- Configure how each step decides where the flow goes next.
Main Fields
| Field | What it controls | What to watch for |
|---|---|---|
| First step | The step where the flow begins. | If this is empty, the chain cannot run. |
| Step name | The label you see while editing the flow. | This is only for readability. It helps a lot once the flow has several branches. |
| Activity | Which activity this step runs before deciding the next route. | Choose "Decision only (no activity)" if the step should only inspect data and branch. |
| Decide the next step by | Which rule this step uses to branch. | The rest of the step changes depending on the condition type you pick. |
| Routes | Where the flow goes after the step resolves. | Routes can point to another step or to "End flow". |
| Flow policies | Safety rules for depth, cancellation, and child errors. | These settings affect the whole chain, not just one step. |
Condition Types
| Condition type | Use it when | Extra fields | Routes shown |
|---|---|---|---|
| Always continue | The step should always go to the same next step. | Next step only. | Next step |
| Actor property | You want to branch from actor data such as HP, spellcasting data, flags, or another deterministic value. | Actor property path, operator, and value. | When true / When false |
| Last activity result | You want to branch from the result of the most recent child activity. | Result path, operator, and value. | When true / When false |
| Roll | The chain itself should ask for a check, save, skill roll, or custom formula roll. | Roll type, ability or skill when needed, and DC. | On success / On failure |
| Manual choice | The user should pick the branch in a dialog. | A list of options. | One route per option |
Canceling a roll or canceling the manual-choice dialog always ends the flow.
Roll Types
| Roll type | What it does | When to use it |
|---|---|---|
| Ability check | Uses the actor's normal D&D 5e ability check workflow for the chosen ability. | Good for broad tests such as Strength or Intelligence checks. |
| Saving throw | Uses the actor's normal D&D 5e saving throw workflow for the chosen ability. | Use it when the branch depends on passing or failing a save. |
| Skill check | Uses the actor's normal D&D 5e skill check workflow for the chosen skill. | Good for flows such as Athletics contests, Stealth gates, or social checks. |
| Custom formula | Rolls a custom formula and posts a chat message for that roll. | Use it when a normal ability or skill roll is not enough. |
The DC field accepts either a number or a deterministic formula such as
8 + @prof or 10 + @scLast.roll.dice.count. The roll succeeds when its total
meets or exceeds that DC.
Last Activity Result
Last activity result means the result from the most recent step that actually ran an activity.
That rule has two important consequences:
- if a step is Decision only (no activity), it does not clear the previous result
- several decision-only steps in a row can branch from different parts of the same stored result
The result picker shows grouped suggestions, but you can switch to Custom path… whenever you need to type a raw path yourself.
When Decide the next step by is Last activity result, the row is made of three parts:
| Field | What it does | Examples |
|---|---|---|
| Result | Chooses which stored value to inspect. | Attack hit, Roll total (all rolls), Grant check passed, Contest winner. |
| Operator | Compares the stored value against the value field. | =, ≠, >, ≥, <, ≤, includes |
| Value | The expected value for the comparison. | 10, true, false, radiant, @abilities.con.mod, @scLast.roll.sum |
What the picker supports:
- for known result types such as attacks, damage or healing rolls,
sc-grant, andsc-contest, the picker can suggest the most useful grouped paths - Custom path… lets you type any raw path, such as
roll.totals.1orroll.dice.values.2 - the Value field accepts numbers,
trueorfalse, text, and deterministic formulas includesworks on text and lists, so it is useful for cases like tags, damage types, or text fragments
Result Path Reference
Use these paths when you need something more specific than the first suggested options.
For custom or unsupported activity types, only the fields that activity really records will return a value. If a path does not exist in the result, the chain cannot evaluate it.
General Paths
These are the most common fields and are available on every tracked result.
| Shown as | Path | What it means |
|---|---|---|
| Result kind | kind | What the last activity produced, such as attack, damage, healing, grant, or contest. |
| Was a success | success | True when the activity succeeded. |
| Was a failure | failure | True when the activity failed. |
| Main total | total | The main total from the result, when one exists. |
| Was a critical | critical | True when the main roll was a critical. |
| Was a fumble | fumble | True when the main roll was a fumble or natural 1. |
| Target number (DC or AC) | target | The DC or armor class the main roll was compared against. |
| Was canceled | canceled | True when the activity was canceled before finishing. |
| Source activity id | sourceActivity.id | Internal id of the activity that produced the result. |
| Source activity type | sourceActivity.type | Type of the activity that produced the result. |
| Source activity name | sourceActivity.name | Display name of the activity that produced the result. |
| Source item id | sourceActivity.itemId | Id of the item that owns the source activity. |
| Source item UUID | sourceActivity.itemUuid | UUID of the item that owns the source activity. |
| Source actor UUID | sourceActivity.actorUuid | UUID of the actor that used the source activity. |
| Chat message id | use.messageId | Id of the chat message created by the activity, when one exists. |
| Updates applied | use.updateCount | How many document updates the activity applied. |
| Effects created | use.effectCount | How many active effects the activity created. |
| Templates placed | use.templateCount | How many measured templates the activity placed. |
Roll Paths
These are the most useful fields for attack, damage, healing, save, and grant-check style results.
| Shown as | Path | What it means |
|---|---|---|
| Roll total (first roll) | roll.total | Total of the first roll, including modifiers. |
| Roll total (all rolls) | roll.sum | Sum of every roll made by that activity. |
| Total of roll #1 | roll.totals.0 | Total of the first individual roll. Use roll.totals.1, .2, and so on for later rolls. |
| Number of rolls | roll.count | How many separate rolls were made. |
| Roll succeeded | roll.success | True when the roll met its target number. |
| Roll failed | roll.failure | True when the roll missed its target number. |
| Roll was a critical | roll.critical | True when the roll was a critical. |
| Roll was a fumble | roll.fumble | True when the roll was a fumble. |
| Roll target number | roll.target | The DC or AC used for that roll. |
| Roll formula | roll.formula | The formula that was rolled, such as 1d20 + 5. |
| Ability used | roll.ability | Ability key used by the roll, such as str or dex. |
| Skill used | roll.skill | Skill key used by the roll, such as ath or ste. |
| Tool used | roll.tool | Tool key used by the roll when the workflow records one. |
Individual Dice Paths
These fields inspect the kept dice only. Discarded dice from advantage, rerolls, and similar effects are ignored.
| Shown as | Path | What it means |
|---|---|---|
| Dice total (no modifiers) | roll.dice.total | Sum of the dice only, without flat bonuses. |
| Number of dice | roll.dice.count | How many kept dice were recorded across all rolls. |
| First die value | roll.dice.values.0 | Face rolled on the first kept die. Use .1, .2, and so on for later dice. |
| Highest die | roll.dice.max | Highest kept die result. |
| Lowest die | roll.dice.min | Lowest kept die result. |
Attack Paths
Use these when the previous child activity is an attack activity.
| Shown as | Path | What it means |
|---|---|---|
| Attack hit | attack.hit | True when the attack hit its target or rolled a critical. |
| Attack missed | attack.miss | True when the attack missed. |
| Attack roll total | attack.total | Total of the attack roll, including modifiers. |
| Critical hit | attack.critical | True when the attack roll was a critical. |
| Attack fumble | attack.fumble | True when the attack roll was a fumble. |
| Target armor class | attack.target | Armor class the attack was compared against for a single target. |
Grant Paths
Use these when the previous child activity is SC Grant.
| Shown as | Path | What it means |
|---|---|---|
| Grant check passed | activity.checkPassed | True when the grant's gating check succeeded. |
| Grant check DC | activity.check.dc | DC used by the grant check. |
| Grant check total | activity.check.total | Total rolled for the grant check. |
| Documents created | activity.createdCount | How many documents the grant created. |
| Documents updated | activity.updatedCount | How many documents the grant updated. |
| Target actor UUID | activity.actorUuid | UUID of the actor who received the grant. |
| Activity canceled | activity.canceled | True when the grant was canceled before finishing. |
| Cancel reason | activity.reason | Why the grant canceled, such as missing actor or check-canceled. |
Contest Paths
Use these when the previous child activity is SC Contest.
| Shown as | Path | What it means |
|---|---|---|
| Contest winner | activity.winner or contest.winner | Who won: initiator, defender, or empty on a tie. |
| Contest tied | activity.tied or contest.tied | True when the contest ended in a tie. |
| Contest attempt number | activity.attempt | How many attempts were made, including a reroll on ties. |
| Initiator roll total | contest.initiator.total | Total rolled by the initiator. |
| Initiator actor UUID | contest.initiator.actorUuid | UUID of the initiator's actor. |
| Initiator token UUID | contest.initiator.tokenUuid | UUID of the initiator's token. |
| Defender roll total | contest.defender.total | Total rolled by the defender. |
| Defender actor UUID | contest.defender.actorUuid | UUID of the defender's actor. |
| Defender token UUID | contest.defender.tokenUuid | UUID of the defender's token. |
Using @scLast In Formulas
The previous result is also exposed to formulas as @scLast.
That is useful when a later roll should reuse the number produced by an earlier step. A common example is a heal or bonus-damage activity that copies the previous damage total.
This heal formula uses @scLast.roll.sum, so it can read the total rolled by
the previous step.
Practical formula notes:
@scLast.roll.sumis the safest way to reuse the previous total across multi-part rolls@scLast.attack.hitand other booleans resolve as1or0inside formulas@scLastby itself resolves to the previous main numeric total when one is available
Flow Policies
The collapsible Flow policies section controls global safety rules for the chain.
These policies affect the whole chain, not just the currently selected step.
| Policy | What it does | Important detail |
|---|---|---|
| Depth limit | Stops execution when the chain goes deeper than the configured nested limit. | This limit is shared with SC Chain. |
| Stop when a child activity is canceled | Ends the chain if a step's activity returns no result. | This does not change manual-choice or roll cancellation. Those always end the flow. |
| Continue after child errors | Keeps routing even if a child activity is missing or throws an error. | Leave this off unless you intentionally want a fallback branch after a broken child step. |
Example Flow: Attack, Then Damage, Then Reuse The Damage Total
This is a practical setup for a non-technical item builder:
- Create an Attack activity on the item.
- Create a Damage activity on the same item.
- Create a Heal or bonus effect activity that uses
@scLast.roll.sumin its formula if you want it to copy the previous damage total. - In SC Conditional Chain, make the first step run Attack and branch
on Last activity result with
Attack hit = true. - Send the true route to a Damage step and the false route to End flow.
- After Damage, either continue directly to another step or add a new
branch that checks
roll.sum,roll.dice.max, or any other value you need.
Activity Creation Flow
SC - More Activities keeps the normal dnd5e activity workflow intact and
extends the creation dialog instead of replacing it.
When supported activity types are present, the dialog is grouped into:
- D&D 5e for native activity types
- Shattered Codex for the built-in SC activity set
- External for third-party modules that register through the SC registry
This matters for both users and builders:
- GMs can distinguish native types from SC or third-party additions
- module authors can register external activity groups without writing directly
into
CONFIG.DND5E.activityTypes - disabled activity types are filtered out of creation and blocked from use
The creation dialog keeps native D&D 5e types visible, then adds a separate Shattered Codex group for the built-in SC activity set.
Module Settings
From Configure Settings > Module Settings > SC - More Activities, the module adds visible settings and menus for both GM administration and personal preview preferences.
| Setting or menu | Scope | What it does |
|---|---|---|
| Debug logging | Client | Logs lifecycle and diagnostic messages to the browser console for your user. |
| Migration backup retention | World, GM | Controls how many migration backups are stored in world settings. Default: 3. Range: 1 to 10. |
| Activity catalog | World, GM | Opens the registered activity catalog and diagnostics window. |
| More Activities migration | World, GM | Opens the preview, apply, export, and restore workflow for legacy more-activities data. |
| Preview colors | Client | Opens the per-user color configuration for teleport, movement, and wall overlays. |
| Documentation | World, GM | Opens the wiki page for this module from inside Foundry. |
| Support the developer | World, GM | Opens the Shattered Codex Patreon support link. |
The module also stores hidden world or client data for disabled activity types, preview colors, and migration backups. Those are managed through the UI and are not intended for direct manual editing.
Activity Catalog
The Activity Catalog is the GM-facing control center for registered activity types.
It includes:
- summary cards for Registered, D&D Ready, Rejected, and Warnings
- filters for search, category, status, and availability
- separate sections for Registered Activities, Rejected Registrations, and Warnings
- actions to Refresh, Copy report, Clear filters, and open Migration
Availability states are important:
- Active means the activity type is available for creation and use
- Disabled means the GM has blocked that type for the world
- Unavailable means the type is not in a state that can be enabled for use
Only activity types that successfully reach the D&D-ready state can be toggled for world use. This makes the catalog useful both as a user control panel and as a diagnostic report for module integrations.
The catalog combines registration state, lifecycle metadata, capability markers, and per-type availability controls in one GM-facing window.
Preview Colors
The Preview colors menu is a client-scoped preference, so each user can pick their own overlay colors without changing the world for everyone else.
It is used by the canvas-oriented workflows:
- SC Teleport
- SC Movement
- SC Wall
The menu includes:
- separate sections for Teleport, Movement, and Wall
Fill colorandBorder colorcontrols- a Live Preview area
- reset and save actions
These colors affect preview overlays only. They are not an activity authoring setting and they do not change the underlying activity data.
Preview colors are saved per user, so one GM can change overlay colors without changing the world for everyone else.
Migration from More Activities
SC - More Activities ships with an explicit migration flow for the legacy
more-activities module.
The migration window is GM-only and split into four tabs:
- Overview
- Preview
- Apply
- Backups
Recommended workflow:
- Keep the legacy module enabled only long enough to inspect or migrate old data.
- Open More Activities migration from module settings.
- Run Preview first.
- Review convertible and blocked entries.
- Use Apply migration only after the preview looks correct.
- Export the preview or final report if you need a record.
- Use Restore latest backup if you need to roll back the last migration.
Important limits:
- migration does not run automatically
- a preview is required before apply
- migration backups are stored in world settings
- some blocked or partially compatible legacy entries may still need manual cleanup after conversion
- the module warns when the legacy
more-activitiesmodule is still active
The preview report makes it clear which legacy entries can be converted, which SC type they map to, and what still needs manual review.
Developer Registration API
If you are building another module, register activity types through the SC
registry instead of mutating CONFIG.DND5E.activityTypes directly.
Public API access:
game.modules.get("sc-more-activities")?.apiglobalThis.ShatteredCodex.activitiesfor the published activities API
Main lifecycle hooks:
sc-more-activities.registerActivitiessc-more-activities.registryLockedsc-more-activities.apiReadysc-more-activities.activityRegisteredsc-more-activities.activityAvailabilityChangedsc-more-activities.activityUseStartsc-more-activities.activityUseCompletesc-more-activities.activityUseError
Registration timing:
- register during
Hooks.on("sc-more-activities.registerActivities", ...) - late registrations are rejected after the registry locks during initialization
- if you need the final published API object, listen for
sc-more-activities.apiReady
Minimum registration contract:
moduleIdtypelabelhinticondocumentClass
Your documentClass must provide:
static metadata.typethat matches the registered typestatic availableForItem(...)static localize(...)
Minimal example:
Hooks.on("sc-more-activities.registerActivities", (activities) => {
activities.registerType({
moduleId: "my-module",
type: "my-module-ignite",
label: "MYMODULE.Activity.Ignite.Label",
hint: "MYMODULE.Activity.Ignite.Hint",
icon: "modules/my-module/icons/ignite.svg",
documentClass: MyIgniteActivity,
});
});The public activities API also exposes helper methods such as:
listTypes()listTypeAvailability()setTypeEnabled(type, enabled)getRegistrationReport()createActivityOnItem(item, type, data, options)
Developer Example: SC Simple Sockets
If you want a concrete external-module example, SC - Simple Sockets is the best reference currently in the Shattered Codex module set.
It does not patch sc-more-activities directly. Instead, it keeps the entire
integration inside its own module and registers two external activity types when
both modules are active:
sc-socket-slotsc-socket-extraction
This example focuses on sc-socket-slot, because it shows the full lifecycle
for a custom activity type.
What Files The Example Uses
| File | Role in the integration |
|---|---|
| `scripts/main.js` | Imports the integration and calls `ScMoreActivitiesIntegration.register()` during module startup. |
| `scripts/core/integrations/sc-more-activities/ScMoreActivitiesConstants.js` | Defines hook names, type ids, group metadata, query id, timeout, and icon paths. |
| `scripts/core/integrations/sc-more-activities/ScMoreActivitiesIntegration.js` | Listens to the registration hook, registers types, exposes GM-mediated query handlers, and dispatches socket mutations. |
| `scripts/core/integrations/sc-more-activities/activities/socket-slot/ScMoreActivitiesSocketSlotActivity.js` | Defines the activity document class and its `use()` behavior. |
| `scripts/core/integrations/sc-more-activities/activities/socket-slot/ScMoreActivitiesSocketSlotActivityData.js` | Defines the activity schema stored on the item. |
| `scripts/core/integrations/sc-more-activities/activities/socket-slot/ScMoreActivitiesSocketSlotActivitySheet.js` | Extends the activity sheet and prepares UI context for the custom effect tab. |
| `scripts/core/integrations/sc-more-activities/activities/socket-slot/ScMoreActivitiesSocketSlotActivityService.js` | Runs the real workflow after `super.use()` succeeds. |
| `templates/integrations/sc-more-activities/socket-slot-effect.hbs` | Renders the custom form fields shown on the activity sheet. |
How The Bootstrap Works
SC - Simple Sockets loads the integration from its own scripts/main.js and
registers it at startup:
import { ScMoreActivitiesIntegration } from "./core/integrations/sc-more-activities/ScMoreActivitiesIntegration.js";
ScMoreActivitiesIntegration.register();That register() method does two jobs:
- It listens to
sc-more-activities.registerActivities. - It registers a query handler during
initso socket mutations can be routed through an active GM when needed.
This is important because the integration owns both the activity definition and the mutation flow. The external module is responsible for its own runtime behavior.
The Real Registration Call
Inside ScMoreActivitiesIntegration.js, SC - Simple Sockets registers
sc-socket-slot like this:
return activitiesApi.registerType({
moduleId: Constants.MODULE_ID,
type: SC_MORE_ACTIVITIES_ACTIVITY_TYPES.SOCKET_SLOT,
label: "SCSockets.Integrations.ScMoreActivities.SocketSlot.Title",
hint: "SCSockets.Integrations.ScMoreActivities.SocketSlot.Hint",
icon: SC_MORE_ACTIVITIES_ICONS.SOCKET_SLOT,
documentClass: ScMoreActivitiesSocketSlotActivity,
dataModel: ScMoreActivitiesSocketSlotActivityData,
sheetClass: ScMoreActivitiesSocketSlotActivitySheet,
configurable: true,
category: "sockets",
ui: {
scope: "external",
group: "sockets",
groupId: SC_MORE_ACTIVITIES_GROUP.id,
groupLabel: SC_MORE_ACTIVITIES_GROUP.label,
groupIcon: SC_MORE_ACTIVITIES_GROUP.icon,
groupOrder: SC_MORE_ACTIVITIES_GROUP.order,
order: 140
},
tags: ["sockets", "slot", "inventory"],
compatibility: {
dnd5e: "5.x",
scMoreActivities: { moduleId: "sc-more-activities", required: true },
scSimpleSockets: { moduleId: "sc-simple-sockets", required: true }
},
templates: [
"modules/sc-simple-sockets/templates/integrations/sc-more-activities/socket-slot-effect.hbs"
],
ownership: {
execute: "item-owner",
hostItem: "activity-item",
mutation: "gm-mediated"
},
source: Constants.MODULE_ID
});Why this registration is useful:
ui.scope: "external"places the type in the external registration flowgroupId,groupLabel, andgroupOrdercreate a module-specific activity group in the create dialogdataModel,sheetClass, andtemplatesturn the type into a configurable authoring experience instead of a bare runtime documentownership.mutation: "gm-mediated"matches the real workflow, because socket mutations may need GM execution
The Activity Class
The activity class extends the standard dnd5e activity mixin and connects the
registered type id to the custom schema, metadata, availability check, and
runtime execution:
export class ScMoreActivitiesSocketSlotActivity extends dnd5e.documents.activity.ActivityMixin(
ScMoreActivitiesSocketSlotActivityData
) {
static metadata = Object.freeze(
foundry.utils.mergeObject(super.metadata, {
type: SC_MORE_ACTIVITIES_ACTIVITY_TYPES.SOCKET_SLOT,
img: SC_MORE_ACTIVITIES_ICONS.SOCKET_SLOT,
title: "SCSockets.Integrations.ScMoreActivities.SocketSlot.Title",
hint: "SCSockets.Integrations.ScMoreActivities.SocketSlot.Hint",
sheetClass: ScMoreActivitiesSocketSlotActivitySheet
}, { inplace: false })
);
static availableForItem(item, ...args) {
const base = typeof super.availableForItem === "function"
? super.availableForItem(item, ...args)
: true;
return base && ScMoreActivitiesIntegration.isTypeEnabled(
SC_MORE_ACTIVITIES_ACTIVITY_TYPES.SOCKET_SLOT
);
}
async use(usage = {}, dialog = {}, message = {}) {
const results = await super.use(usage, dialog, message);
if (results === undefined) return results;
return ScMoreActivitiesSocketSlotActivityService.execute(this, { usage, dialog, message, results });
}
}The pattern to copy into your own module is:
- Extend
dnd5e.documents.activity.ActivityMixin(...). - Put the registered type id into
static metadata.type. - Route availability through
availableForItem(...). - Call
super.use(...)first. - Hand off the actual side effects to a dedicated service class.
The Data Model
ScMoreActivitiesSocketSlotActivityData extends
dnd5e.dataModels.activity.BaseActivityData and stores all custom fields under
slot.
Real schema fields in the example include:
slot.operationslot.nameslot.colorslot.cursorImageslot.descriptionslot.conditionslot.targetConditionslot.hiddenslot.deleteGemOnRemovalslot.ignoreMaxSockets
That structure is worth copying because it keeps custom activity state grouped under one schema branch instead of scattering unrelated top-level fields.
The Sheet and Template
ScMoreActivitiesSocketSlotActivitySheet extends
dnd5e.applications.activity.ActivitySheet and injects a custom effect-part
template:
static PARTS = {
...super.PARTS,
effect: {
template: TEMPLATE_PATH,
templates: [...super.PARTS.effect.templates]
}
};That template is
templates/integrations/sc-more-activities/socket-slot-effect.hbs, and it
renders the actual authoring controls for:
- slot operation
- slot name and color
- cursor image
- description
- condition code
- target condition code
- behavior toggles such as hidden, delete on removal, and ignore socket limit
Use this pattern when your custom activity needs real item-authoring UI instead of a small number of primitive fields.
The Runtime Service
The actual socket behavior does not live in the activity class. It lives in
ScMoreActivitiesSocketSlotActivityService.
That service decides whether the configured operation is:
- add a slot to a selected item
- remove one empty slot from the host item
The service also validates the target condition, launches the slot-picker UI
when needed, and hands the mutation back to ScMoreActivitiesIntegration.
That separation matters:
- the activity class stays small and predictable
- the data model only describes stored state
- the service owns workflow branching, validation, dialogs, and user feedback
How GM-Mediated Execution Works
SC - Simple Sockets is a strong example because it shows how to handle
mutations that may require GM authority.
Inside ScMoreActivitiesIntegration.js, the integration:
- Builds a request payload with
activityUuid,itemUuid,operation, andrequestUserId. - Executes directly when the current user is a GM or already has socket-edit permission.
- Falls back to an active GM query using a registered query id when direct execution is not allowed.
- Resolves the request on the GM side and turns the result into a normalized success or failure object.
This is the concrete pattern to follow when your custom activity mutates actors, items, scenes, or other documents that should not be changed blindly by every player client.
Minimal Blueprint For Your Own Module
If you are building a new external activity, the smallest practical file set is:
scripts/main.js
scripts/integrations/sc-more-activities/MyIntegration.js
scripts/integrations/sc-more-activities/MyActivity.js
scripts/integrations/sc-more-activities/MyActivityData.js
scripts/integrations/sc-more-activities/MyActivitySheet.js
scripts/integrations/sc-more-activities/MyActivityService.js
templates/integrations/sc-more-activities/my-activity-effect.hbsBuild it in this order:
- Register an integration class from
scripts/main.js. - Listen to
sc-more-activities.registerActivities. - Call
activitiesApi.registerType(...). - Create a document class with
metadata,availableForItem(...), anduse(...). - Add a data model for your stored fields.
- Add a sheet class and Handlebars template for configuration.
- Move the actual side effects into a service class.
- Add GM-mediated dispatch only if your workflow really needs it.
When To Copy This Pattern
Use the sc-simple-sockets structure when your activity:
- needs custom authoring fields
- performs complex runtime logic after
super.use(...) - may require GM-mediated document changes
- belongs to a module-specific activity group in the create dialog
- should stay fully owned by the integrating module instead of by
sc-more-activities
Notes and Limits
- This module supports only the
dnd5esystem. - It extends native activities instead of replacing them.
- World macros, inline code, hooks, and callbacks still follow their own permission and ownership limits.
- Canvas actions such as teleport, movement, and wall placement rely on GM-mediated scene updates.
- The migration workflow is explicit by design, which is safer than an automatic conversion but still requires review before apply.