scmJS docs

Changing a map

Each command here writes the map back over itself, or to another file with --out other.scx, and keeps everything else in the file as it was: its sounds and other files, and the way the scenario was compressed, as Save does in the editor.

Name, description and revision#

scmjs map set is Map Properties. It sets what you give and leaves the rest, and writes nothing when nothing changed.

scmjs map set ridge.scx --name "Ridge <04>2.1" --description "Final for the tournament."
scmjs map set ridge.scx --revision remastered
scmjs map set ridge.scx --encoding euc-kr
✔ ridge.scx: Name ridge → Ridge · written to ridge.scx · 3.0 KB

In a name or description, <XX> is one of the game's colour codes, as in the string files. --revision is one of original, hybrid, broodwar, remastered and remasteredHybrid, and does what the editor's revision change does. --encoding is how the map's text is stored for 1.16.1: utf-8, euc-kr, shift_jis, gbk, big5, windows-1251 or windows-1252. Remastered reads UTF-8.

Resizing#

scmjs map resize ridge.scx --size 128x96 is Scenario ▸ Resize.

✔ ridge.scx: 96 × 64 → 128 × 96 · written to ridge.scx · 3.6 KB

--anchor says which part of the map stays where it is: center (the default), top-left, top, top-right, left, right, bottom-left, bottom or bottom-right. Units and other things that fall outside the new edge are dropped, and the command lists them. Locations that hang over the edge are pulled inside unless you pass --no-clamp. New ground is the tileset's base terrain.

Triggers and strings as text#

A map's triggers and strings can live beside it as text files. In git a change to them shows as a diff; in any text editor you can search and replace across a hundred triggers. Then they go back into the map.

scmjs map triggers export mymap.scx > triggers.txt
scmjs map triggers import mymap.scx triggers.txt

triggers export prints the triggers in the text form the editor's trigger window uses (the trigger reference lists every condition and action in it), or writes them to --out. A file name ending in .trg writes the raw trigger records instead: the file SCMDraft's Export Triggers makes and reads. --briefing exports the mission briefing's triggers.

triggers import replaces the map's triggers with the file's, or adds them after the map's own with --append. Text the map cannot resolve, such as a condition it does not know or a location it does not have, is refused with its line number, and nothing is written.

scmjs map strings export mymap.scx > strings.txt
scmjs map strings import mymap.scx strings.txt

The strings file has one string a line: the string's number, a tab, and its text, with the game's colour codes written as <XX>. A # starts a comment. strings import sets each numbered string in place and adds any numbered past the end. Nothing is renumbered, because the map's triggers and locations refer to strings by number; a line you take out leaves that string as it is.

Editing triggers or strings in your editor#

edit exports, opens the text in your editor, and imports it when you close the editor:

scmjs map triggers edit mymap.scx
scmjs map strings edit mymap.scx --editor "code --wait"

The command waits until the editor is closed. If you saved a change, the text is imported as import reads it and the map is written. If you saved nothing, or only a comment, the map is left alone and the command says nothing changed.

Text that cannot be read, such as a condition the map does not know or a strings line that is not a number and a tab, is not imported and the map is left alone. The error gives the line. The file you edited is kept with your changes in it, and the command prints its path and the import command that brings it in once you have fixed it.

The editor is --editor, or else the VISUAL or EDITOR setting as git uses them, or else Notepad on Windows and vi elsewhere. An editor that opens a window has to be told to wait for it to close, or the command sees the file unchanged straight away: code --wait, subl -w, gedit -s. triggers edit --briefing edits the briefing's triggers.

Removing unused strings#

scmjs map strings prune ridge.scx is the string editor's Delete Unused. A string that no trigger, location, name, force, sound or setting refers to is blanked, and the blank slots at the end of the table are dropped. Nothing is renumbered, so the triggers still point at the right strings. --dry-run lists what would go and writes nothing.

Making a map smaller#

scmjs map optimize takes out of a map what the game never uses and stores the rest as small as the game still reads it. It writes over the map unless --out names another file; --dry-run says what would go and writes nothing. A table says what each step saved:

$ scmjs map optimize "(3)Defenders of the Galaxy.scm"
✔ (3)Defenders of the Galaxy.scm: 44 KB → 36 KB (−17.7%) · written to (3)Defenders of the Galaxy.scm

  Step                                           Saved
  Unused sounds    none                          -
  Other files      none                          -
  Unused strings   23 strings                    243 B
  Editor sections  skipped: with --strip-editor  -
  Compression      PKWARE, rewritten             7.5 KB

Removed
  string    21 - Defend your Nexus at all costs.  nothing in the map refers to it
  …

In order, the steps are:

Some files stay whatever refers to them. The game itself opens a few files by name: (attributes), (signature) and the marks Blizzard's own maps and the ladder maps carry, such as blizzard_2.cnd. TrigScript's, Magenta's and the editor's build files stay (trigscript\, magenta\, scmjs\), as do files the archive holds without a name. A sound StarEdit imported is stored with the game's own sound compression, which loses a little quality to be about four times smaller; the editor reads it but cannot write it, and writing it any other way would make the map larger, so such a file is kept exactly as it is stored. --keep names other files to keep: --keep "notes\" keeps a folder.

A map built from a TrigScript script keeps its scenario as it was built, so the editor still opens the map you were editing; only its stray files and its compression change. A map whose triggers write to the game's memory (an EUD map) keeps every string, since such a map can reach a string by its place in the table.

--strip-editor also drops ISOM, TILE, DD2, IVER, IVE2, IOWN, UPUS, SWNM and WAV, the sections the CHK reference marks as read by editors only, with the strings and sounds only they named. The map plays the same, but an editor that opens it has no isometric terrain to paint, no doodads to select, no sound list and no switch names. It is the Save dialog's Smallest that plays for these sections; keep the original to go on editing.

The result is opened again as the editor opens a map before anything is written, and when the editor cannot write the map any smaller, nothing is written. scmjs map diff between the two shows only what went.

The archive as a folder#

A map file is an archive: the scenario, staredit\scenario.chk, with the map's sounds, a TrigScript script's files and anything else put in it. scmjs map unpack writes those files into a folder at their paths in the archive, and scmjs map pack puts the folder back together:

scmjs map unpack ridge.scx            # → ridge/staredit/scenario.chk, ridge/staredit/wav/…, ridge/archive.json
scmjs map unpack ridge.scx --list     # the files and their sizes, nothing written
scmjs map pack ridge                  # → ridge.scx again, beside the folder

archive.json in the folder records what the files cannot: the map's name, how the scenario was compressed and whether it was encrypted, whether the archive had a file list, and the files a protected map keeps without a name, which go under stored/ byte for byte. pack writes the archive as archive.json says, so an unpacked and repacked map reads the same in the editor and the game.

A file you add to the folder goes into the map at its path: a sound dropped into staredit/wav/, a script's file under trigscript/. --compression none|pkware|zlib changes how every file is stored, and --out names the folder or the file to write.

Nothing is parsed or restored on the way: the folder is the file as it is. A map the editor built from a TrigScript script unpacks into its built scenario and the scmjs/ files holding the source, and packs back into the same built map. The packed file is opened as a map before it is written, so a folder whose scenario is not a scenario is refused.

With --text, the scenario itself is written as text files under scenario/ instead of the one binary staredit/scenario.chk, so the map can be kept in git and read and merged as text. A map as a folder of text explains the folder.

Building a TrigScript map#

The editor builds a map with a TrigScript script every time it saves it: the script's trigger() calls become triggers in the map, and its program()s are built into the file. scmjs map build does the same without the editor, so a map kept in git can be built where it is checked, and a script edited outside the editor can be applied from the terminal.

$ scmjs map build maps/ridge.scx --test
✔ ridge.scx: 12 triggers → #4–#15 · 2 programs (Remastered) · 7 tests passed · written to maps/ridge.scx · 48 KB

The script is the one the map keeps, with the copies of libraries the map keeps. It is compiled against the map's names, settings and placed units, as the editor compiles it. The built file opens in the editor as the map you were editing, with the build kept beside it, as a saved one does.

Option What it does
--out Writes the built map to another file
--test Runs the script's tests first, in the simulator; a failing test stops the build. A map whose script settings make tests guard the build runs them without the flag
--check Compiles (and tests, with --test) and writes nothing: the check for a pull request

An error in the script is printed as main.ts:12:5 error …, a failing test with its name and line, and the command exits with 1 with nothing written.

The compiler is TrigScript's own, the version this release of the editor ships, so the command and the editor build the same map. It compiles with TypeScript, which is not part of the command: install it in the map's project (npm install typescript) or globally (npm install -g typescript). A sound of two seconds or more is encoded as Ogg Vorbis with an encoder the command fetches once, unless the project has wasm-media-encoders installed.

Two things differ from the editor. In the tests, a unit type's hit points come only from the map's own unit settings: the editor falls back on the game's unit data for a type the map leaves at its defaults, and the command does not have that data. A test that counts on a Marine's 40 hit points should set them in the map. And programs that other plugins add to a build are not built here: a map with Magenta's triggers is refused, and is built by saving it in the editor.

Running a plugin over a map#

scmjs map run gives a plugin the editor's plugin API with a map open and no editor around it. The plugin can be a folder with a plugin.json, the manifest itself, a single script file, github:owner/repo or a URL: whatever Plugins ▸ Add takes, and a file on your disk too. A script is a file that exports activate(api) as a plugin's entry does, so a few lines that read or change a map need no plugin around them:

// starts.mjs
export default (api) => {
  const starts = api.document.scenario().units.filter((u) => u.unitId === 214);
  api.ui.status(`${api.document.info().name}: ${starts.length} start locations`);
};
scmjs map run starts.mjs "(4)Crossing.scx"
scmjs map run ../plugin-repair maps/ridge.scx --command repair --dry-run

What the plugin would show is printed as it happens: its status line, its toasts, and the warnings and errors in its log. Dialogs and panels register and show nothing, and a request for a file gets none. --command <id> runs one of the commands the plugin registered, with --arg values as its arguments; --json includes what the command returned.

The map is written back only when the plugin changed it (--out writes it elsewhere, even unchanged), and the last line says what changed, in the terms of map diff. --dry-run prints that comparison in full and writes nothing. Saving is the command's job: api.document.save() has no file picker to open here. A plugin written in TypeScript needs TypeScript installed, as map build does; a built plugin or a JavaScript script needs nothing more.

This is the editor's own plugin host, so a plugin's logic runs as it does in the editor. You can try a plugin from a terminal, or run it over many maps from a shell loop, before anyone installs it.

scmJS 0.12.1 · 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.