api.triggers
TriggersApi
Reading triggers, and the pure helpers that make them presentable. Writing goes
through document.update.
- list
- briefing
- switchNames
- switchUsage
- names
- defs
- text
- newTrigger
- newCondition
- newAction
- isPreserved
- setPreserved
- triggersFor
- summarize
- comment
- epd
- addressOf
- fingerprint
- usage
- references
- resolvePlayers
- claim
- claims
Members
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 itcomment
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 itreferences
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 itresolvePlayers
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 itclaims
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";
ConditionFieldtype
type ConditionField = keyof ConditionRecord;
ActionFieldtype
type ActionField = keyof ActionRecord;
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 |
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 |
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 |
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 |
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
( |
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 |
address?: number |
|
mask?: number |
|
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 itTriggerTextApiinterface
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 |
one(trigger: ReadonlyTrigger, options?: { briefing?: boolean; }): string | One trigger as its |
// 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 itTriggerClaimRangeinterface
interface TriggerClaimRange
One claimed run as triggers.claims located it.
pluginId: string | |
|---|---|
label: string |
|
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: |
|---|---|
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 |
describe?(index: number, list: readonly ReadonlyTrigger[]): string | A sentence about one trigger of the run (its index in |
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; |
TriggerClaimHandleinterface
interface TriggerClaimHandle
refresh(): void | The editors ask |
|---|---|
remove(): void |