Skip to content

Timeline Commands Reference

SuitIThub edited this page Jul 6, 2026 · 4 revisions

Timeline commands reference

Command type IDs are defined in TimelineCommandFactory.Create(). Each command serializes to JSON in timeline.json with a "type" field matching these IDs.

Every entry also has an "enabled" flag (default true). Disabled rows are skipped at run time but stay in the list for organization.

Invalid configuration is highlighted on the row (red) with a tooltip from GetValidationError. Run is blocked while any enabled command is invalid.

Commands can be edited from Studio or via the Timeline Web Remote browser UI (structured fields per command type).

Table of contents

  1. Input simulation
  2. Flow control
  3. Sub-timelines
  4. CopyScript
  5. Screenshots
  6. Studio / scene
  7. Pose
  8. Variables & logic
  9. VNGE
  10. Other
  11. Adding new commands

Input simulation

Type ID Command Purpose
simulate_key SimulateKeyCommand Send key combo (Ctrl+A, F5, etc.)
simulate_mouse SimulateMouseCommand Mouse button click
move_mouse MoveMouseCommand Move cursor to position
scroll ScrollCommand Mouse wheel scroll

Flow control

Type ID Command Purpose
pause PauseCommand Wait for duration (ms; interpolatable)
loop LoopCommand Repeat section
checkpoint CheckpointCommand Mark checkpoint
jump JumpToCheckpointCommand Jump to checkpoint
confirm ConfirmCommand Wait for user Confirm in the list
resolve_on_issue ResolveOnIssueCommand Wait if CopyScript has open issues
resolve_on_count ResolveOnCountCommand Wait if tracked file count ≠ expected
sub_timeline SubTimelineCommand Run nested timeline (see Sub-timelines)
sub_timeline_param SubTimelineParamCommand Declare one parameter for a sub-timeline
sub_if SubBlockIfCommand Start of a block-if (main timeline or sub-timeline)
sub_elseif SubBlockElseIfCommand Additional conditional branch
sub_else SubBlockElseCommand Default branch
sub_endif SubBlockEndIfCommand End of the block-if
return ReturnCommand Exit sub-timeline, or stop the run at root
label LabelCommand Visual separator / section label (no-op at run time)

pause — duration in milliseconds. The field accepts a literal or a variable/expression (same int resolution as calc). If the value cannot be resolved, a Resolve button appears on the row.

confirm — blocks until you click Confirm on that row while the timeline is running. Refocus (default on) simulates a click at (0,0) afterward to release UI focus.

resolve_on_issue — calls CopyScript GET /api/issues. If issues_count > 0, shows Resolve; clicking it releases focus and continues.

resolve_on_count — calls CopyScript GET /api/tracking and compares total_count to the Expected field (interpolatable int). On mismatch, waits for Resolve like resolve_on_issue.

return — inside a sub-timeline, pops back to the parent list after the sub_timeline row. On the main timeline, stops execution (same as Stop).

label — draws a horizontal rule and optional centered text for organization. Lv (level 0 = darkest) controls row shading. Does nothing when executed.

loop — jumps to a named checkpoint up to Repeat times per run (interpolatable). The row label shows the current iteration while running, e.g. Loop (2).

Block-if (sub_if / sub_elseif / sub_else / sub_endif)

A structural if / else if* / else / end construct that works on the main timeline and inside sub-timelines (the add buttons live in the Subtimeline category). Blocks may be repeated and nested freely. Unlike the checkpoint-based while, these branch by position in the list rather than by jumping to a named checkpoint.

Rules

  • Every sub_if must be balanced by a matching sub_endif (End If is mandatory). Between them: any number of sub_elseif, then at most one sub_else. Order within a block must be IfElse If* → ElseEnd If. Nested blocks are matched by depth like brackets.
  • Violations are shown on the offending row and block Start.
  • The conditions in sub_if / sub_elseif behave exactly like the while command (same operands, operators, Not).
  • sub_else and sub_endif have no condition.

Execution

  • If true → run the commands directly below it; when the block reaches the next same-level marker (Else If / Else), jump to its matching End If.
  • If false → evaluate each Else If in turn; the first true one runs its block. If none match, the Else block runs.
  • After a taken branch finishes, execution continues after End If.

Example "CheckMood" (works the same on the main timeline or in a sub-timeline)

1. Set        mood = "happy"     (normal command)
2. If         mood == "happy"
3.   Pose Apply "smile"
4. Else If    mood == "sad"
5.   Pose Apply "cry"
6. Else
7.   Pose Apply "neutral"
8. End If
9. Screenshot                    (runs after the block)
  • mood = "happy" → 1 → 2 (true) → 3 → jump to 8 → 9
  • mood = "sad" → 1 → 2 (false) → 4 (true) → 5 → jump to 8 → 9
  • mood = "angry" → 1 → 2 (false) → 4 (false) → 6 (else) → 7 → 8 → 9

Sub-timelines (sub_timeline / sub_timeline_param)

Sub-timeline bodies are stored in a root-level subtimelines array in timeline.json (keyed by stable id). Each sub_timeline row references one body by id and shows its title, command count, and an button to edit the nested list.

Templates — mark a definition as Tpl (template). Other rows can Ref-link to the same body by matching title, so multiple timeline rows share one nested script. Only one Param command is allowed per sub-timeline.

Parameters — add sub_timeline_param once inside the sub-timeline (types: str, int, bool, list, dict). The parent sub_timeline row shows inputs for that parameter; values are applied when execution enters the sub-timeline (before other sub-commands). Variables set inside a sub-timeline remain visible in the parent after it returns.

Validation — sub-command errors bubble up to the parent sub_timeline row. Block-if rules inside sub-timelines use the same structure as on the main timeline.

CopyScript

Type ID Command Purpose
start_tracking StartTrackingCommand Start file tracking
stop_tracking StopTrackingCommand Stop tracking
copy_rename CopyRenameCommand Copy/rename tracked files
clear_tracked ClearTrackedFilesCommand Clear tracked list
set_source_path SetSourcePathCommand Set source path
set_destination_path SetDestinationPathCommand Set destination
set_name_pattern SetNamePatternCommand Set rename pattern
set_rule_counter SetCounterRuleCommand Counter rule
set_rule_list SetListRuleCommand List rule
set_rule_batch SetBatchRuleCommand Batch rule

copy_renameOverwrite (off by default) is passed to CopyScript POST /api/copy_rename. On API failure the row shows Resolve and retries when clicked.

set_rule_list — tag + step like other rules. Values come from an inline list (Edit list…) or, when the var toggle is on, from a list variable name at run time.

Screenshots

Requires the BepInEx ScreenshotManager plugin (com.bepis.bepinex.screenshotmanager). The obsolete wait_screenshot command was removed; old timelines drop those rows automatically on load.

Type ID Command Purpose
screenshot ScreenshotCommand Trigger capture and wait for a confirmed new file
wait_empty_screenshots WaitForEmptyScreenshotsCommand Wait until CopyScript tracking queue is empty
screenshot_alpha ScreenshotAlphaModeCommand Alpha mode
screenshot_resolution ScreenshotResolutionCommand Resolution
screenshot_save_path ScreenshotSavePathCommand Save path
screenshot_alt_path_var ScreenshotAltPathVarCommand Set alt save path variable

screenshot — triggers TakeRenderScreenshot via ScreenshotManager, then waits until a new PNG/JPEG appears in the active save folder (plugin completion API and/or filesystem watch with stable file-size checks). Retries the trigger up to 12 times, then keeps polling and slow-retriggering until a file is confirmed or you click Continue on the row (logged as a sequence error — timeline proceeds without a verified file).

Alt path — when enabled, temporarily sets the plugin save folder from the timeline variable ScreenshotAltRelPath for this shot only (set beforehand with screenshot_alt_path_var or any set string). Restores the previous path afterward. Requires the Screenshot plugin static path API.

CopyScript Check — label shows Screenshot CS. After each capture, verifies CopyScript tracking: the newest tracked file must match the screenshot and exist on disk (up to 3 attempts per round; failed shots are removed from tracking and deleted). On failure, Resolve retries the round. Requires the CopyScript API client.

Successful captures notify Studio Live Screenshot-Follow (/api/studio/screenshot-latest).

wait_empty_screenshots — polls CopyScript GET /api/tracking?count=1 every second until returned_count is 0 (queue drained). Use after copy_rename batches before the next capture wave.

screenshot_alt_path_var — writes an interpolatable game-relative folder path into ScreenshotAltRelPath (e.g. UserData/cap_paperdoll). Used when screenshot runs with Alt path enabled.

Studio / scene

Type ID Command Purpose
clothing_state ClothingStateCommand Per-part clothing On / Half / Off
accessory_state AccessoryStateCommand Accessory states
gdc_wardrobe_state GdcWardrobeStateCommand Enable/disable all GDC Wardrobe layers
outfit_rotate OutfitRotateCommand Rotate outfit
outfit_by_name OutfitByNameCommand Select outfit by name
set_camera_by_name SetCameraByNameCommand Select camera
select_object_by_name SelectObjectByNameCommand Select workspace object
set_object_visible_by_name SetObjectVisibleByNameCommand Toggle visibility
replace_chara_card ReplaceCharaCardCommand Load chara from UserData/chara/
load_coordinate_card LoadCoordinateCardCommand Load coordinate card
pose_library PoseLibraryCommand Pose library interaction
sound SoundCommand Play sound

clothing_statePart cycles cached Studio clothing parts (or free text if the cache is empty). State is On / Half / Off (three-state parts) or On / Off (two-state parts). Applied directly to currently selected character(s) via OCIChar; the manipulate panel does not need to be open. No selection → warning, no-op.

accessory_stateSlot cycles Studio accessory slots (All Slots first when available). State is On or Off. Same selection rules as clothing.

gdc_wardrobe_state enables (On) or disables (Off) all layers of the GDC Wardrobe plugin at once. Each GDC layer is a GameObject on the character named GDCLayer_*; the plugin's per-layer toggle just calls SetActive on it, so the command does the same directly on every such GameObject (across all characters, including currently inactive ones). The GDC window does not need to be open, but it also won't refresh its own checkboxes when toggled this way.

outfit_by_name — calls FashionLine WearFashionByNameForActiveCharacters (requires prolo.fashionline). File (default on): name is a FashionLine outfit file; off = outfit stored on the character card. Reload (default off): full ChaControl reload after apply — enable only when needed; leaving it off avoids FK/IK corruption over many outfit changes.

select_object_by_name — resolves a workspace object by name (same tree as other object commands). Mode: Select (deselect others, then select), Deselect, or Toggle (default).

set_object_visible_by_nameVisible accepts true/false, a bool variable, or interpolation (same rules as bool operands elsewhere).

Pose

Type ID Command Purpose
pose_apply PoseApplyCommand Apply a Pose Browser pose by path (interpolatable). Optionally target a single character by name
pose_apply_group PoseApplyGroupCommand Apply a Pose Browser pose group by path. Optionally target a set of characters by name

Path for pose_apply is the pose file's path relative to studio/pose (e.g. MyFolder/cool pose.png); an absolute path also works. Path for pose_apply_group is the group name (the group id also works). Both are obtained via the Copy path button in the Pose Browser (on a pose or on a group). Requires the Pose Browser plugin to be installed.

  • pose_apply: with Character off it applies to the workspace selection; with Character on it looks up the single scene character with the given (interpolatable) name and applies the pose to that character.
  • pose_apply_group: with Characters off it applies to the workspace selection; with Characters on the character set comes either from a list variable (whose items are character names) or from an inline list edited in the command. Names are resolved to scene characters. The Pose Browser priority list is applied in all cases.

Variables & logic

Type ID Command Purpose
set SetVariableCommand Set scalar variable (str / int / bool kind selector)
set_string SetStringCommand (legacy) Set string; loads old timelines
set_integer SetIntegerCommand (legacy) Set integer; loads old timelines
get GetVariableCommand Copy variable with optional type conversion
calc CalcCommand Arithmetic
if WhileCommand Conditional jump to checkpoint (do-while loop)
str_replace StrReplaceCommand String replace
str_split StrSplitCommand Split a string into a list variable
list ListCommand List operations
range RangeCommand Cycle a variable through a set of numbers/ranges
set_list SetListCommand Set list variable
list_insert (legacy) Folded into list (insert mode); auto-migrated on load
list_remove (legacy) Folded into list (remove mode); auto-migrated on load
list_apply_dict ListApplyDictCommand Apply dict to list
dict_set DictSetCommand Dictionary set
dict_get DictGetCommand Dictionary get

setKind cycles str / int / bool. Value supports interpolation; int/bool operands that cannot be resolved wait for Resolve at run time. Legacy set_string / set_integer timelines still load; new saves use set.

get — copies FromTo with As (str / int / bool) for conversion.

Condition operators (used by while and the block-if commands): ==, , <, , >, , plus three membership operators where the right field is a variable name (a list, a dict, or a string holding a range expression):

  • ∈F — left equals the first element (list) / first number (range).
  • ∈L — left equals the last element (list) / last number (range).
  • in — left is contained in the right: any element of a list, any number of a range, or a key or value of a dict.

Unresolved condition operands at run time show Resolve on the row (same as pause / set int).

list is the all-in-one list command. The first field is the Variable for read modes (the result is written there) or the List variable for mutate modes (it is changed in place). A mode button cycles through:

  • Read modes (write a result into the variable, sourced from an inline list or a list variable):
    • next (default) — advance through the values, cycling.
    • rev — advance from last to first.
    • first / last — always the first / last value.
    • get — read the value at a specific Idx (always reads from a list variable; the inline-list option is hidden). A 1-based toggle switches between 0-based (default) and 1-based indexing; an out-of-range index is skipped with a warning.
    • length — store the number of items as a number (works on an empty list too, stores 0).
    • find — store the index of the value typed in the Find field (interpolatable), or -1 if absent. The 1-based toggle offsets a found index by one.
  • Mutate modes (change the list variable named in the first field, in place):
    • set — replace the element at Idx with the Value (1-based toggle supported); out-of-range is skipped with a warning.
    • insert — add a value, with a sub-mode button: At index (insert at an interpolatable/variable Idx, clamped to bounds), Append (end), Prepend (front), or Extend (the value field, labelled From, names another list variable whose contents are appended in order).
    • remove — remove elements, with a sub-mode button: By Index (interpolatable/variable Idx, out-of-range skipped with a warning), By Value (remove the first matching Value), or Clear (empty the whole list).

The legacy list_insert and list_remove commands are the insert and remove modes; old timelines that use them are migrated to list automatically on load. List/variable names, indices and values all support interpolation.

range drives a variable from a sequence of numbers each time it runs. The sequence comes from an interpolatable text field of comma-separated single numbers and/or inclusive ranges: 1-10, 1,2,3,5, 1-3,6, 1-10,12,14-20. A descending range like 5-1 counts down. A mode button selects the behaviour:

  • next (default) — write the next value, cycling. 1-51, 2, 3, 4, 5, 1, … (stored as int).
  • rev — like next but from the last value to the first. 1-55, 4, 3, 2, 1, 5, … (int).
  • first — always the first value of the whole range (int).
  • last — always the last value of the whole range (int).
  • list — write the whole expanded range into a list variable, e.g. 1-3,6[1, 2, 3, 6].

The target variable is created if it doesn't exist yet (an int for next/rev/first/last, a list for list mode).

str_split splits a Source string by a Separator and writes the parts into a List var (created if absent). Source and separator both support interpolation (e.g. {myVar}). An empty separator yields a single-element list containing the whole source. Pairs naturally with list in get mode to read individual parts.

str_replace — replaces Find with Replace inside a string variable (all fields interpolatable). All (default) replaces every match; First replaces only the first.

VNGE (requires modified VNGE)

Type ID Command Purpose
vnge_scene_next VngeSceneNextCommand Next scene
vnge_scene_prev VngeScenePrevCommand Previous scene
vnge_next_scene VngeNextSceneCommand Next scene alt
vnge_prev_scene VngePrevSceneCommand Prev scene alt
vnge_load_scene VngeLoadSceneByIndexCommand Load scene by index

Other

Type ID Command Purpose
video_record VideoRecordCommand Video recording
get_fashion GetFashionCommand FashionLine data
display DisplayCommand Open/close a floating window showing a variable's live value

display takes a variable name (type string, int, or bool) and a mode button: open (default) or close. In open mode it opens a movable, resizable window with the variable name (top) and its current value (large, below); the value updates live while the timeline runs. Each window has a close button and a pin button (top-right) — pinning persists that window's position/size for the variable across runs and sessions. Opening a window for a variable that already has one is a no-op. In close mode it closes the window for that variable. All Display windows also close automatically when the timeline ends. Open cards are mirrored on the Web Remote Dashboard.

Instead of a variable name you can enter an override token. Currently supported:

  • %TIME — elapsed run time of the timeline, formatted HH:MM:SS.

Overrides are defined in DisplayOverrides.cs; add an entry there to extend the set.

Adding new commands (developers)

See .cursor/skills/create-timeline-command/ in the repository:

  1. Create src/Timeline/YourCommand.cs extending TimelineCommand
  2. Register type ID in TimelineCommandFactory.cs
  3. Add UI in ActionTimelineWindow if needed
  4. Optional: AppendEditFields in src/Timeline/Web/CommandWebFields.*.cs for Web Remote editors

Navigation: ← Timeline · Timeline commands · Web Remote · Next: SearchBarManager →

Clone this wiki locally