Shattered Codex

Wiki

Modules/SC - Conditional Activities

SC - Conditional Activities

Foundry v13-v14System D&D 5eFree

SC - Conditional Activities

Overview

SC - Conditional Activities adds a Condition tab to D&D 5e activity sheets in Foundry VTT.

The condition is a JavaScript snippet that decides whether the activity can currently be used. When the script returns true, the activity works normally. When it returns false, the activity stays visible but is marked Not available and is blocked before activity.use() can continue.

Use it when an activity should depend on:

  • actor state, such as HP, ability scores, resources, or flags
  • item state, such as equipped, attuned, identified, or custom flags
  • the current user or GM-only rules
  • selected targets
  • combat state
  • activity type
  • socket data from SC - Simple Sockets

The module also works with activity types registered by More Activities.

Installation

This module is free.

Install it through Foundry with this manifest URL:

code
https://github.com/Shattered-Codex/sc-conditional-activities/releases/latest/download/module.json

After installing:

  1. Enable SC - Conditional Activities in your world.
  2. Open an item with one or more dnd5e activities.
  3. Edit an activity.
  4. Open the Condition tab.
  5. Add a JavaScript condition and save the activity.

Compatibility:

  • Foundry VTT v13 and v14
  • system: dnd5e
  • dnd5e system version: 5.0.0+
  • optional: More Activities activity types

Activity Condition

Every supported activity sheet gets a dedicated Condition tab.

Activity condition tab with a Simple Sockets condition script

The Condition tab uses a JavaScript editor and links back to this documentation page from inside Foundry.

The condition is stored on the activity at:

code
flags.sc-conditional-activities.condition

Empty conditions always allow the activity. Non-empty conditions are evaluated when the UI checks availability and again when the activity is used.

Where Locked Activities Appear

Locked activities remain visible so players can see what exists and why an option is not currently usable.

Activity choice dialog with Teleport marked as Not available
Activity choice
When an item has multiple activities, blocked choices remain in the activity picker with a Not available label.
1 / 3

The module decorates activity rows in:

  • dnd5e activity choice dialogs
  • item sheet activity lists
  • actor sheet activity lists
  • chat activity controls when an activity can be resolved

If the condition throws an error, the activity is treated as unavailable and receives a Condition error label instead.

Condition Context

Conditions receive a limited set of variables:

  • activity: the current activity document
  • activityType: the activity type, such as attack, save, or a custom type
  • item: the item that owns the activity
  • actor: the actor that owns the item, when available
  • user: the current user
  • usage: cloned usage data passed into activity.use()
  • dialog: cloned dialog data passed into activity.use()
  • message: cloned message data passed into activity.use()
  • rollData: the owning item's roll data, when available
  • source: the evaluation source, such as ui or use
  • getProperty: Foundry's foundry.utils.getProperty
  • hasProperty: Foundry's foundry.utils.hasProperty
  • deepClone: Foundry's foundry.utils.deepClone
  • game: the Foundry game object

Writing Conditions

You can write a complete script:

js
return actor?.system?.attributes?.hp?.value > 0;

You can also write a single expression without return:

js
actor?.system?.attributes?.hp?.value > 0

Expression shorthand only works when the entire condition is one expression. If you use statements such as const, let, if, or await, finish with return.

This works:

js
const hp = actor?.system?.attributes?.hp?.value ?? 0;
return hp > 0;

This does not work:

js
const hp = actor?.system?.attributes?.hp?.value ?? 0;
hp > 0;

Conditions can be asynchronous:

js
const document = await fromUuid("Actor.example.Item.example");
return Boolean(document);

Example Conditions

Actor Must Be Alive

js
return actor?.system?.attributes?.hp?.value > 0;

Only The GM Can Use This Activity

js
return user?.isGM === true;

Item Must Be Equipped

js
return item?.system?.equipped === true;

Item Must Be Attuned

js
return item?.system?.attuned === true;

Item Must Be Identified

js
return item?.system?.identified === true;

Actor Must Have At Least 10 HP

js
return actor?.system?.attributes?.hp?.value >= 10;

Actor Must Have A Specific Flag

js
return getProperty(actor, "flags.world.canUseAncientPower") === true;

Activity Must Be An Attack Activity

js
return activityType === "attack";

Item Name Must Include A Word

js
return item?.name?.toLowerCase().includes("flame");

Actor Must Have A Minimum Strength Score

js
return actor?.system?.abilities?.str?.value >= 16;

Actor Must Have A Resource Available

js
return actor?.system?.resources?.primary?.value > 0;

Require At Least One Target Selected

js
return game.user?.targets?.size > 0;

Require Exactly One Target Selected

js
return game.user?.targets?.size === 1;

Require Combat To Be Active

js
return Boolean(game.combat?.started);

Simple Sockets Examples

These examples are useful when the activity belongs to an item that also uses SC - Simple Sockets.

Item Must Have At Least One Occupied Socket

Recommended version using the Simple Sockets API:

js
const socketsApi = game.modules.get("sc-simple-sockets")?.api?.sockets;
if (!socketsApi) return false;
 
const slots = await socketsApi.getItemSlots(item);
return slots.some((entry) => entry.hasGem);

First Socket Must Be Occupied

js
const socketsApi = game.modules.get("sc-simple-sockets")?.api?.sockets;
if (!socketsApi) return false;
 
const slots = await socketsApi.getItemSlots(item);
return slots[0]?.hasGem === true;

Item Must Have At Least Two Occupied Sockets

js
const socketsApi = game.modules.get("sc-simple-sockets")?.api?.sockets;
if (!socketsApi) return false;
 
const slots = await socketsApi.getItemSlots(item);
return slots.filter((entry) => entry.hasGem).length >= 2;

Item Must Have A Gem With A Specific Name

js
const socketsApi = game.modules.get("sc-simple-sockets")?.api?.sockets;
if (!socketsApi) return false;
 
const gems = await socketsApi.getItemGems(item);
return gems.some((gem) => gem.name?.toLowerCase().includes("ruby"));

Direct Flag Check For An Occupied Socket

Use this only if you want a simple direct read and do not need the Simple Sockets API helpers.

js
const sockets = item?.getFlag?.("sc-simple-sockets", "sockets") ?? [];
return sockets.some((slot) => Boolean(slot?.gem));

Notes and Limits

  • Empty conditions always allow the activity.
  • If a condition returns a falsy value, the activity is unavailable.
  • If a condition throws an error, the activity is unavailable and the user sees a condition error warning.
  • Conditions run when the UI checks availability and again when the activity is used.
  • Conditions are JavaScript and should be treated like other trusted Foundry scripting surfaces.
  • A condition can use await, but slow conditions can make activity lists feel slower because labels are evaluated during render.