Shattered Codex

Wiki

Modules/SC - More Activities

SC - More Activities

Foundry v13-v14System D&D 5eFree

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 dnd5e activity 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

  1. In Foundry VTT, open Add-on Modules.
  2. Choose Install Module.
  3. Paste this manifest URL:
code
https://github.com/Shattered-Codex/sc-more-activities/releases/latest/download/module.json
  1. Install the module.
  2. Enable SC - More Activities in your world.
  3. Install and enable libWrapper if you want the recommended compatibility layer.

Compatibility:

  • Foundry VTT v13 to v14
  • system: dnd5e
  • dnd5e compatibility declared in module.json: minimum 5.0.0, verified 5.3.0
  • localization: English and Brazilian Portuguese

Important behavior:

  • the module exits early outside dnd5e worlds
  • native dnd5e activities remain available
  • the module relies on items and sheets that already support the dnd5e activity 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-activities activity 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

ActivityUse it forMain configurationImportant notes
SC SoundPlay 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 MacroRun 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 HookEmit 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 ChainExecute 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 ChainBranch 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 ContestResolve 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 GrantGrant 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 AdvancementTrigger 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 TeleportChoose 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 MovementPush 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 WallCreate 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 Advancement
Use a source item that already contains dnd5e advancements, then select the advancement entries this activity can trigger.
1 / 11

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:

  1. Create the attack, damage, healing, grant, macro, or other activities you want the item to use.
  2. Add SC Conditional Chain.
  3. Open the chain's Effect tab.
  4. Add your steps in the order you want to think about them.
  5. Pick the First step.
  6. Configure how each step decides where the flow goes next.

Main Fields

FieldWhat it controlsWhat to watch for
First stepThe step where the flow begins.If this is empty, the chain cannot run.
Step nameThe label you see while editing the flow.This is only for readability. It helps a lot once the flow has several branches.
ActivityWhich 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 byWhich rule this step uses to branch.The rest of the step changes depending on the condition type you pick.
RoutesWhere the flow goes after the step resolves.Routes can point to another step or to "End flow".
Flow policiesSafety rules for depth, cancellation, and child errors.These settings affect the whole chain, not just one step.

Condition Types

Condition typeUse it whenExtra fieldsRoutes shown
Always continueThe step should always go to the same next step.Next step only.Next step
Actor propertyYou 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 resultYou want to branch from the result of the most recent child activity.Result path, operator, and value.When true / When false
RollThe 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 choiceThe 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 typeWhat it doesWhen to use it
Ability checkUses 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 throwUses 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 checkUses 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 formulaRolls 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:

FieldWhat it doesExamples
ResultChooses which stored value to inspect.Attack hit, Roll total (all rolls), Grant check passed, Contest winner.
OperatorCompares the stored value against the value field.=, ≠, >, ≥, <, ≤, includes
ValueThe 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, and sc-contest, the picker can suggest the most useful grouped paths
  • Custom path… lets you type any raw path, such as roll.totals.1 or roll.dice.values.2
  • the Value field accepts numbers, true or false, text, and deterministic formulas
  • includes works 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 asPathWhat it means
Result kindkindWhat the last activity produced, such as attack, damage, healing, grant, or contest.
Was a successsuccessTrue when the activity succeeded.
Was a failurefailureTrue when the activity failed.
Main totaltotalThe main total from the result, when one exists.
Was a criticalcriticalTrue when the main roll was a critical.
Was a fumblefumbleTrue when the main roll was a fumble or natural 1.
Target number (DC or AC)targetThe DC or armor class the main roll was compared against.
Was canceledcanceledTrue when the activity was canceled before finishing.
Source activity idsourceActivity.idInternal id of the activity that produced the result.
Source activity typesourceActivity.typeType of the activity that produced the result.
Source activity namesourceActivity.nameDisplay name of the activity that produced the result.
Source item idsourceActivity.itemIdId of the item that owns the source activity.
Source item UUIDsourceActivity.itemUuidUUID of the item that owns the source activity.
Source actor UUIDsourceActivity.actorUuidUUID of the actor that used the source activity.
Chat message iduse.messageIdId of the chat message created by the activity, when one exists.
Updates applieduse.updateCountHow many document updates the activity applied.
Effects createduse.effectCountHow many active effects the activity created.
Templates placeduse.templateCountHow 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 asPathWhat it means
Roll total (first roll)roll.totalTotal of the first roll, including modifiers.
Roll total (all rolls)roll.sumSum of every roll made by that activity.
Total of roll #1roll.totals.0Total of the first individual roll. Use roll.totals.1, .2, and so on for later rolls.
Number of rollsroll.countHow many separate rolls were made.
Roll succeededroll.successTrue when the roll met its target number.
Roll failedroll.failureTrue when the roll missed its target number.
Roll was a criticalroll.criticalTrue when the roll was a critical.
Roll was a fumbleroll.fumbleTrue when the roll was a fumble.
Roll target numberroll.targetThe DC or AC used for that roll.
Roll formularoll.formulaThe formula that was rolled, such as 1d20 + 5.
Ability usedroll.abilityAbility key used by the roll, such as str or dex.
Skill usedroll.skillSkill key used by the roll, such as ath or ste.
Tool usedroll.toolTool 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 asPathWhat it means
Dice total (no modifiers)roll.dice.totalSum of the dice only, without flat bonuses.
Number of diceroll.dice.countHow many kept dice were recorded across all rolls.
First die valueroll.dice.values.0Face rolled on the first kept die. Use .1, .2, and so on for later dice.
Highest dieroll.dice.maxHighest kept die result.
Lowest dieroll.dice.minLowest kept die result.

Attack Paths

Use these when the previous child activity is an attack activity.

Shown asPathWhat it means
Attack hitattack.hitTrue when the attack hit its target or rolled a critical.
Attack missedattack.missTrue when the attack missed.
Attack roll totalattack.totalTotal of the attack roll, including modifiers.
Critical hitattack.criticalTrue when the attack roll was a critical.
Attack fumbleattack.fumbleTrue when the attack roll was a fumble.
Target armor classattack.targetArmor class the attack was compared against for a single target.

Grant Paths

Use these when the previous child activity is SC Grant.

Shown asPathWhat it means
Grant check passedactivity.checkPassedTrue when the grant's gating check succeeded.
Grant check DCactivity.check.dcDC used by the grant check.
Grant check totalactivity.check.totalTotal rolled for the grant check.
Documents createdactivity.createdCountHow many documents the grant created.
Documents updatedactivity.updatedCountHow many documents the grant updated.
Target actor UUIDactivity.actorUuidUUID of the actor who received the grant.
Activity canceledactivity.canceledTrue when the grant was canceled before finishing.
Cancel reasonactivity.reasonWhy the grant canceled, such as missing actor or check-canceled.

Contest Paths

Use these when the previous child activity is SC Contest.

Shown asPathWhat it means
Contest winneractivity.winner or contest.winnerWho won: initiator, defender, or empty on a tie.
Contest tiedactivity.tied or contest.tiedTrue when the contest ended in a tie.
Contest attempt numberactivity.attemptHow many attempts were made, including a reroll on ties.
Initiator roll totalcontest.initiator.totalTotal rolled by the initiator.
Initiator actor UUIDcontest.initiator.actorUuidUUID of the initiator's actor.
Initiator token UUIDcontest.initiator.tokenUuidUUID of the initiator's token.
Defender roll totalcontest.defender.totalTotal rolled by the defender.
Defender actor UUIDcontest.defender.actorUuidUUID of the defender's actor.
Defender token UUIDcontest.defender.tokenUuidUUID 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.sum is the safest way to reuse the previous total across multi-part rolls
  • @scLast.attack.hit and other booleans resolve as 1 or 0 inside formulas
  • @scLast by 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.

PolicyWhat it doesImportant detail
Depth limitStops execution when the chain goes deeper than the configured nested limit.This limit is shared with SC Chain.
Stop when a child activity is canceledEnds 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 errorsKeeps 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:

  1. Create an Attack activity on the item.
  2. Create a Damage activity on the same item.
  3. Create a Heal or bonus effect activity that uses @scLast.roll.sum in its formula if you want it to copy the previous damage total.
  4. In SC Conditional Chain, make the first step run Attack and branch on Last activity result with Attack hit = true.
  5. Send the true route to a Damage step and the false route to End flow.
  6. 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 menuScopeWhat it does
Debug loggingClientLogs lifecycle and diagnostic messages to the browser console for your user.
Migration backup retentionWorld, GMControls how many migration backups are stored in world settings. Default: 3. Range: 1 to 10.
Activity catalogWorld, GMOpens the registered activity catalog and diagnostics window.
More Activities migrationWorld, GMOpens the preview, apply, export, and restore workflow for legacy more-activities data.
Preview colorsClientOpens the per-user color configuration for teleport, movement, and wall overlays.
DocumentationWorld, GMOpens the wiki page for this module from inside Foundry.
Support the developerWorld, GMOpens 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 color and Border color controls
  • 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:

  1. Keep the legacy module enabled only long enough to inspect or migrate old data.
  2. Open More Activities migration from module settings.
  3. Run Preview first.
  4. Review convertible and blocked entries.
  5. Use Apply migration only after the preview looks correct.
  6. Export the preview or final report if you need a record.
  7. 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-activities module 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")?.api
  • globalThis.ShatteredCodex.activities for the published activities API

Main lifecycle hooks:

  • sc-more-activities.registerActivities
  • sc-more-activities.registryLocked
  • sc-more-activities.apiReady
  • sc-more-activities.activityRegistered
  • sc-more-activities.activityAvailabilityChanged
  • sc-more-activities.activityUseStart
  • sc-more-activities.activityUseComplete
  • sc-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:

  • moduleId
  • type
  • label
  • hint
  • icon
  • documentClass

Your documentClass must provide:

  • static metadata.type that matches the registered type
  • static availableForItem(...)
  • static localize(...)

Minimal example:

js
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-slot
  • sc-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

FileRole 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:

js
import { ScMoreActivitiesIntegration } from "./core/integrations/sc-more-activities/ScMoreActivitiesIntegration.js";
 
ScMoreActivitiesIntegration.register();

That register() method does two jobs:

  1. It listens to sc-more-activities.registerActivities.
  2. It registers a query handler during init so 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:

js
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 flow
  • groupId, groupLabel, and groupOrder create a module-specific activity group in the create dialog
  • dataModel, sheetClass, and templates turn the type into a configurable authoring experience instead of a bare runtime document
  • ownership.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:

js
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:

  1. Extend dnd5e.documents.activity.ActivityMixin(...).
  2. Put the registered type id into static metadata.type.
  3. Route availability through availableForItem(...).
  4. Call super.use(...) first.
  5. 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.operation
  • slot.name
  • slot.color
  • slot.cursorImage
  • slot.description
  • slot.condition
  • slot.targetCondition
  • slot.hidden
  • slot.deleteGemOnRemoval
  • slot.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:

js
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:

  1. Builds a request payload with activityUuid, itemUuid, operation, and requestUserId.
  2. Executes directly when the current user is a GM or already has socket-edit permission.
  3. Falls back to an active GM query using a registered query id when direct execution is not allowed.
  4. 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:

code
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.hbs

Build it in this order:

  1. Register an integration class from scripts/main.js.
  2. Listen to sc-more-activities.registerActivities.
  3. Call activitiesApi.registerType(...).
  4. Create a document class with metadata, availableForItem(...), and use(...).
  5. Add a data model for your stored fields.
  6. Add a sheet class and Handlebars template for configuration.
  7. Move the actual side effects into a service class.
  8. 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 dnd5e system.
  • 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.