scmJS docs

api.triggers

TriggersApi

Reading triggers, and the pure helpers that make them presentable. Writing goes through document.update.

Members

list

list(): TriggerRecord[];

TRIG, cloned.

briefing

briefing(): TriggerRecord[];

MBRF, cloned.

switchNames

switchNames(): string[];

SWNM as names, StarEdit's Switch N where there is none.

switchUsage

switchUsage(): number[];

How many conditions and actions mention each switch.

names

names(): TriggerNames;

The name context the text format resolves against: the map's locations, units, switches and strings.

defs

readonly defs: TriggerDefsApi;

text

readonly text: TriggerTextApi;

newTrigger

newTrigger(players?: readonly number[]): TriggerRecord;

An empty trigger owned by the given player groups (All Players by default).

newCondition

newCondition(type: number): ConditionRecord;

A condition of a type, with StarEdit's defaults for its arguments.

newAction

newAction(type: number, briefing?: boolean): ActionRecord;

isPreserved

isPreserved(trigger: ReadonlyTrigger): boolean;

setPreserved

setPreserved(trigger: ReadonlyTrigger, on: boolean): TriggerRecord;

triggersFor

triggersFor(list: readonly ReadonlyTrigger[], groups: readonly number[]): number[];

The indices of the triggers any of these player groups own.

summarize

summarize(trigger: ReadonlyTrigger, briefing?: boolean): { players: string; conditions: string; actions: string; };

The three lines the trigger list shows: players, conditions, actions.

for (const trigger of api.triggers.list()) {
  const line = api.triggers.summarize(trigger);
  api.log(`${line.players}: ${line.conditions} → ${line.actions}`);
}
Try it

comment

comment(trigger: ReadonlyTrigger): string | null;

A trigger's Comment action text, when it has one.

epd

epd(address: number): number;

EUD: the player value that reaches a memory address through the deaths table ((address − consts.triggers.deathsTable) / 4, the address rounded down to a dword) — what a Deaths condition or Set Deaths action stores in player with unitId 0 to read or write that dword.

addressOf

addressOf(player: number, unitId?: number): number;

EUD: the address a record's player and unitId reach through the deaths table — epd's inverse, and a plain death counter's cell as an address.

fingerprint

fingerprint(trigger: ReadonlyTrigger): string;

A content fingerprint of a trigger: the same wherever it sits in the list and whatever the game's bookkeeping bits say. What a plugin that generates triggers finds its run by (TriggerClaimSpec.locate), and what an editor keeps a selection by across a list that changed under it.

usage

usage(list?: readonly ReadonlyTrigger[]): { cells: [ player: number, unit: number ][]; switches: number[]; };

Every death-counter cell ([player, unit]) and switch the triggers read or write — TRIG when list is omitted. A player group counts as every slot it can resolve to (Current Player as the trigger's owners); a class unit (Any unit, Men, …) counts for nothing, and an EUD player is an address, not a cell. What a plugin that allocates counters or switches of its own must keep clear of.

// A switch no trigger touches, for a plugin that needs one of its own.
const taken = new Set(api.triggers.usage().switches);
const free = Array.from({ length: 256 }, (_, i) => i).find((i) => !taken.has(i));
api.log(free === undefined ? "every switch is in use" : `${api.names.switch(free)} is free`);
Try it

references

references(list?: readonly ReadonlyTrigger[], options?: { briefing?: boolean; }): TriggerRefs[];

What each trigger reads, writes and names — TRIG when list is omitted, one entry per trigger in list order. A condition reads (a switch, a death counter, the timer, a player's ore, units at a location); an action writes (Set Switch, Set Deaths, Create Unit, Move Location's target, Victory) or uses something without changing it (the location units appear at, the text a message shows, a sound). Player groups are resolved to slots against the map's forces, Current Player as the trigger's owners; a Deaths record past the counters is a memory reference with its address.

What a plugin needs to draw how triggers depend on each other, find a switch that is set and never tested, or answer "which triggers can end the game". With briefing, MBRF, where only strings and sounds are referred to.

// Switches something tests that nothing ever sets.
const refs = api.triggers.references().flatMap((t) => t.refs).filter((r) => r.kind === "switch");
const set = new Set(refs.filter((r) => r.access === "write").map((r) => r.id));
const never = [...new Set(refs.filter((r) => r.access === "read" && !set.has(r.id)).map((r) => r.id))];
Try it

resolvePlayers

resolvePlayers(group: number, owners?: readonly number[]): { players: readonly number[]; approximate: boolean; };

The 0-based player slots a player group names on the open map: a slot itself, the members of a force, players 1–8 for All Players, and for Current Player the slots of owners (the groups a trigger is ticked for). approximate when the group is only settled while the game runs (Foes, Allies, Neutral Players, Non Allied Victory Players); players is then every slot it could name.

claim

claim(spec: TriggerClaimSpec): TriggerClaimHandle;

Tell the editor that a run of the trigger list is generated by this plugin. The Trigger Editor badges those rows, locks them and offers the plugin's own editor in place of the form; the Text Trigger Editor fences them in comments; Import Triggers says what a replace would remove. The claim lives until remove() or the plugin's deactivation; call refresh() after a rebuild so the editors ask locate again.

// Fingerprints of the triggers this plugin generated, kept as it generates them.
const mine = new Set<string>();
const isMine = (t: Parameters<typeof api.triggers.fingerprint>[0]) => mine.has(api.triggers.fingerprint(t));
const claim = api.triggers.claim({
  label: "the wave generator",
  badge: "waves",
  locate(list) {
    const start = list.findIndex(isMine);
    if (start < 0) return null;
    let count = 1;
    while (start + count < list.length && isMine(list[start + count])) count++;
    return { start, count };
  },
  describe: () => "Made by the wave generator. Change the waves there, not here.",
});
// After regenerating: update `mine`, then
claim.refresh();
Try it

claims

claims(list?: readonly ReadonlyTrigger[]): TriggerClaimRange[];

Every plugin's claims as located in list (TRIG as it stands, when omitted), first run first — what an editor of the trigger list needs to fence or lock the generated runs. A claim whose locate finds nothing, or throws, is left out.

Types

Declarations only this group names.

ArgKindtype

type ArgKind = "player" | "unit" | "location" | "switch" | "comparison" | "switchState" | "switchAction" | "modifier" | "unitState" | "order" | "alliance" | "resource" | "score" | "aiScript" | "textFlags" | "text" | "wav" | "number" | "amount" | "count" | "duration" | "percent" | "cuwp" | "slot";

ArgDefinterface

interface ArgDef
kind: ArgKind
field: F
label: string

ConditionDefinterface

interface ConditionDef
type: number
name: string
args: ArgDef<ConditionField>[]

ActionDefinterface

interface ActionDef
type: number
name: string
args: ArgDef<ActionField>[]
hasTextFlags?: boolean

Text / Transmission: the AlwaysDisplay flag is an argument of its own.

Choiceinterface

interface Choice
value: number
label: string
aliases?: string[]

Extra spellings the text parser accepts.

TriggerNamesinterface

interface TriggerNames

How the text format names things it cannot know on its own.

string(index: number): string | null

Text of a string-table entry, null when unset.

intern(text: string): number

Index for text — an existing identical entry or a new one. Only called while parsing.

location(number: number): string

Display name of a 1-based location number.

locationByName(name: string): number | undefined

1-based number for a location name (or Location N / Anywhere), undefined when unknown.

unit(id: number): string
unitByName(name: string): number | undefined
switch(index: number): string

Display name of a 0-based switch.

switchByName(name: string): number | undefined

TextTriggerinterface

interface TextTrigger
trigger: TriggerRecord
line: number

1-based line the Trigger( header starts on.

TriggerRefKindtype

type TriggerRefKind = 
/** A switch; `id` is its 0-based number. */
"switch"
/** A death counter; `id` is the unit id, `players` the slots whose cell it is. */
 | "deaths"
/** A death counter out of range — an EUD read or write; `address` is the dword it reaches, `id` the unit value as written. */
 | "memory"
/** A location; `id` is its 0-based slot (63 is Anywhere). */
 | "location"
/** The countdown timer; `id` is 0. */
 | "timer"
/** A player's ore or gas; `id` is the `ResourceType`. */
 | "resources"
/** A player's score; `id` is the `ScoreType`. */
 | "score"
/** Units of a type; `id` is the unit id (a class id for *Any unit*, *Men*, …). */
 | "units"
/** Victory, Defeat or Draw; `id` is the action type, `players` the trigger's owners. */
 | "outcome"
/** A string of the map's string table; `id` is the 1-based string index. */
 | "string"
/** A sound; `id` is the string index of its file name. */
 | "wav"
/** A Run AI Script code (four characters as a little-endian u32). */
 | "aiScript"
/** A Create Unit with Properties slot, 0-based. */
 | "cuwp";

Every kind of thing a condition or action can refer to.

TriggerRefAccesstype

type TriggerRefAccess = "read" | "write" | "use";

read: a condition tests it. write: an action changes it. use: an action names it without changing it — the location units appear at, the text a message shows.

TriggerRefinterface

interface TriggerRef
kind: TriggerRefKind
access: TriggerRefAccess
part: "condition" | "action"

Whether a condition or an action refers to it.

slot: number

The condition's or action's position in the trigger.

type: number

The condition or action type.

id: number
players: number[]

The 0-based player slots it is about, for the kinds that belong to a player (deaths, resources, score, units, outcome); empty for the rest, and for a condition such as Command the Most that compares every player.

group?: number

The player group as the record stores it, where the record has one.

approximate?: boolean

The group resolves only while the game runs (Foes, Allies, Neutral Players, Non Allied Victory Players), so players is every slot it could name.

address?: number

memory: the address reached (rounded down to a dword).

mask?: number

memory: the bits a masked record reaches (its location field), when the record is masked.

disabled?: boolean

The condition or action is switched off (its Disabled flag); the game skips it.

TriggerRefsinterface

interface TriggerRefs
index: number

The trigger's index in the list.

owners: number[]

The 0-based slots the trigger runs for.

groups: number[]

The player groups ticked in the trigger, as stored.

inert: boolean

Set by the Disabled flag, or no player owns it: the game never runs it.

refs: TriggerRef[]

TriggerDefsApiinterface

interface TriggerDefsApi

What each condition and action type takes: the argument list in the order StarEdit's TrigEdit shows it, each naming the record field it lives in and the kind of value it is (a player group, a unit id, a location, a comparison, …). Everything that displays or edits a trigger reads this table, the editor's own dialogs included.

conditions(): ConditionDef[]
condition(type: number): ConditionDef | undefined
actions(briefing?: boolean): ActionDef[]
action(type: number, briefing?: boolean): ActionDef | undefined
choices(kind: ArgKind): Choice[]

The values an enumerated argument kind can take, with their labels.

choiceLabel(kind: ArgKind, value: number): string | undefined
choiceValue(kind: ArgKind, text: string): number | undefined

The value behind a label or one of its aliases, for parsing.

// What the first action of the first trigger is, with the name and kind of each argument.
const action = api.triggers.list()[0]?.actions[0];
const def = action && api.triggers.defs.action(action.type);
if (def) api.log(def.name, def.args.map((a) => `${a.label} (${a.kind}) in .${a.field}`));
Try it

TriggerTextApiinterface

interface TriggerTextApi

Printing and parsing the text trigger format (File ▸ Import / Export ▸ Triggers).

print(triggers: readonly ReadonlyTrigger[], options?: { briefing?: boolean; }): string
parse(source: string, options?: { briefing?: boolean; }): TextTrigger[]

Parse text into records, resolving names against the open map (and interning the strings it mentions). Throws a TriggerTextError carrying the line on bad input.

one(trigger: ReadonlyTrigger, options?: { briefing?: boolean; }): string

One trigger as its Trigger(…) block.

// Save the triggers as text, the form File ▸ Export ▸ Triggers writes.
const text = api.triggers.text.print(api.triggers.list());
await api.ui.saveFile(new Blob([text], { type: "text/plain" }), "triggers.txt");
Try it

TriggerClaimRangeinterface

interface TriggerClaimRange

One claimed run as triggers.claims located it.

pluginId: string
label: string

TriggerClaimSpec.label: what generated the run, as a sentence would use it.

badge: string

The badge word the trigger list shows; the plugin's id by default.

start: number
count: number

TriggerClaimSpecinterface

interface TriggerClaimSpec

A run of triggers a plugin generates and owns. It is found by content: the editors work on copies of the list with their own unsaved inserts and removals, so locate is asked with whatever list an editor holds and answers where the run is in it — by a hash of the records, say — or null when the records are not there.

label: string

What generated the run, in words, as a sentence would use it: "the trigger script". label, badge and openLabel are given in English and shown through the plugin's registered catalogues, so they follow a language change; api.triggers.claims hands back the English.

badge?: string

The word on the badge the trigger list shows on each row; the plugin's id by default.

locate(list: readonly ReadonlyTrigger[]): { start: number; count: number; } | null

Where the run sits in list, or null when it is not there (edited by hand, or gone).

describe?(index: number, list: readonly ReadonlyTrigger[]): string

A sentence about one trigger of the run (its index in list), shown in place of the editor's form.

open?(index: number, list: readonly ReadonlyTrigger[]): void

Open the plugin's own editor on that trigger — the button under the sentence.

openLabel?: string

That button's label; Open <plugin name> by default.

TriggerClaimHandleinterface

interface TriggerClaimHandle
refresh(): void

The editors ask locate again (after a rebuild, or when the run moved).

remove(): void

Seen in

scmJS 0.7.3 + main@e2d8ba5 · Generated from the repository. The source is under the MIT license, which does not cover what ATTRIBUTION.md lists. StarCraft and Brood War are trademarks of Blizzard Entertainment; this project ships none of their data.