Coming from eudplib
This page is for map makers who already write EUD maps with eudplib, in Python or in epScript, and build them with euddraft. A TrigScript program does the same kind of work: code that runs inside StarCraft: Remastered, reading and writing the game's memory. Most of what you know carries over, and this page says where each idea went.
Which one to use#
TrigScript suits you when you want to work in one place: the code is checked against the
map's own names as you type, Simulate and tests try it without
starting the game, and saving the map builds it, in the browser or the desktop app. Its
language is TypeScript, with arrays that grow, classes, closures, try / catch and
async, and the same script can make ordinary triggers for the classic game beside the
programs.
eudplib stays the better fit when a map depends on an euddraft plugin or a community library that TrigScript has nothing like, when you want euddraft's map protection, or when you already have a build you are happy with. TrigScript does not read eudplib or epScript code: moving a map over means writing its code again.
The same ideas, side by side#
| eudplib, epScript | TrigScript |
|---|---|
EUDVariable(), var x = 0; |
let x = 0; inside a program() (Variables) |
PVariable, getcurpl() / setcurpl() |
A per-player program, { owner: AllPlayers }: every variable is per player, and CurrentPlayer is that player (Owners) |
onPluginStart() |
The start of a program's body, before its loop |
beforeTriggerExec(), afterTriggerExec() |
A loop in the program: while (true) { …; await nextFrame(); } (How a program runs) |
EUDIf(), EUDWhile(), if, while in epScript |
if, while, for, switch, with conditions of the game where a boolean goes (Control flow) |
DoActions(…), an action as a statement |
An action as a statement: setDeaths(P1, units.TerranMarine, "set", 0); |
@EUDFunc, function in epScript |
function, called or written out by the compiler (Functions) |
EUDFuncPtr |
A function kept in a variable, and closures (Functions as values) |
EUDArray, EUDVArray |
number[], fixed or growing, with push, map, filter, sort and the rest (Arrays) |
foreach (cunit : EUDLoopCUnit()), CUnit |
for (const u of unitsOf(P1)), u.hp, u.order(…) (Units) |
f_dwread_epd, SetMemory, MemoryEPD |
stats() and memory.* for the named fields; memory() and setMemory() for any address (The game's tables and memory) |
StringBuffer, f_sprintf |
A template literal: displayText(`Wave ${wave}`) (Text) |
| The MSQC plugin (keys and the mouse) | keyPressed, keyHeld, clicked, mouse, with nothing to configure (Keys, the mouse and chat) |
| The chatEvent plugin | chatted(player, "-spawn {n} {what:unit}") and on.chat |
The .eds file and an euddraft run |
Saving the map |
One difference shows up everywhere: a unit's fields. eudplib reads them as the game keeps
them, so hit points are in 256ths and an owner is a number. TrigScript's fields are in the
units the game shows (u.hp is whole hit points, u.owner a player), and only the fields
that have been seen working in Remastered are there.
A counter of lives, in epScript as a euddraft plugin, then in TrigScript:
var lives = 3;
function beforeTriggerExec() {
if (Deaths(P1, AtLeast, 1, "Terran Marine")) {
lives -= 1;
SetDeaths(P1, SetTo, 0, "Terran Marine");
if (lives == 0) {
setcurpl(P1);
DisplayText("Out of lives.");
}
}
}
program(() => {
let lives = 3;
while (true) {
if (deaths(P1, units.TerranMarine, ">=", 1)) {
lives -= 1;
setDeaths(P1, units.TerranMarine, "set", 0);
if (lives == 0) displayText("Out of lives.");
}
sleep(frames(1));
}
});
The program runs as Player 1, so displayText is already Player 1's. Its loop is the
beforeTriggerExec of the epScript, and the sleep gives the frame back.
A loop over units: Player 1's Marines under 20 hit points healed to 40.
function beforeTriggerExec() {
foreach (cunit : EUDLoopCUnit()) {
if (cunit.owner == P1 && cunit.unitType == $U("Terran Marine") && cunit.hp < 20 * 256) {
cunit.hp = 40 * 256;
}
}
}
program(() => {
while (true) {
for (const u of unitsOf(P1, { type: units.TerranMarine })) if (u.hp < 20) u.hp = 40;
sleep(frames(1));
}
});
The epScript examples here were built with euddraft 0.11 and eudplib 0.81.
Speed and size#
We built the same test maps both ways, with TrigScript's own build and with eudplib, and ran them on the same machine. The figures are from October 2026, and they are rounded:
- Building a small test map took under a second in the editor, against about five seconds through euddraft.
- Running: a benchmark of program statements ran in about half eudplib's time, and a loop over units cost about half as much a unit. Arrays, text with numbers in it and tasks came out several times faster. One thing is slower: throwing an error costs about three times what it does in eudplib.
- Map size: TrigScript's maps came out larger on most test maps, usually 1.2 to 1.6 times eudplib's: 2.9 MB against 1.8 MB for the units test map. The tasks test map came out smaller, 1.4 MB against 2.1 MB.
Neither kind of map plays on StarCraft 1.16.1: that version refuses writes past the end of
the deaths table, which such a map makes from its first frame. A script of trigger()
calls alone, with no program, plays on every version.
What does not carry over#
- Map protection. euddraft's freeze scrambles a map against copying. TrigScript adds no protection, by decision: a map stays one the editor, and anyone else's, can open.
- euddraft plugins and Python libraries. TrigScript has its own library of modules, and other scmJS plugins can provide libraries of their own. Neither runs Python.
- Fields nobody has checked.
memory()andsetMemory()reach any address, as eudplib does, but the named fields are only the ones seen working in Remastered. Writing a unit's position, for one, ends a Remastered game with "EUD not supported", so it is not offered. - The community. eudplib has years of maps, guides and libraries behind it, many of them in Korean. TrigScript's own build is new, from October 2026.