Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
363 commits
Select commit Hold shift + click to select a range
cd020c8
TrigScript 4, B5: the selection map, played
jeany55 Oct 18, 2026
3381390
TrigScript 4, B5: the selection map, played in multiplayer
jeany55 Oct 18, 2026
d7e0cef
TrigScript 4, B5: IR 23 in docs/ir.md
jeany55 Oct 18, 2026
f45665d
Merge pull request #19 from scm-js/b5-local
jeany55 Oct 18, 2026
4a4add3
TrigScript 4: closures over locals, increments as values, functions t…
jeany55 Oct 18, 2026
2d07a52
Tests: put back two values the IR version change caught
jeany55 Oct 18, 2026
90e1ae4
TrigScript 4: text and array methods, JSX components, functions from …
jeany55 Oct 18, 2026
b90c11c
TrigScript 4: console tests, and the plan as built
jeany55 Oct 18, 2026
c5dac32
Standard library: trigscript/console is trigscript/screen
jeany55 Oct 18, 2026
30d54ac
TrigScript 4: generics; trigscript/timers, signals, random and math
jeany55 Oct 18, 2026
b350133
Screen.reveal, and ui.lines fills the text lines
jeany55 Oct 18, 2026
cd1f2be
Snake on a 64 x 64 map; the screen's colours in the text lines' order…
jeany55 Oct 18, 2026
3dc1362
Snake: the length meter out of 60, so it fills as the snake grows
jeany55 Oct 18, 2026
f558d9f
Screen: a solid look, each pixel a Pylon
jeany55 Oct 18, 2026
c3b7163
Plan: the solid screen played; dots stay the default
jeany55 Oct 18, 2026
68ec388
Merge pull request #20 from scm-js/closures-text
jeany55 Oct 18, 2026
a1fe218
Plan: what is still missing, and B9, references and values
jeany55 Oct 18, 2026
d915e0b
References (B9): classes kept by reference as rows of the object table
jeany55 Oct 18, 2026
714d85c
References: counting sees past a call standing as a statement; tests …
jeany55 Oct 18, 2026
94440df
References: the sweep asks a row's class once and clears only its col…
jeany55 Oct 18, 2026
ba48c25
Values (B9): records and instances of classes nothing changes in plac…
jeany55 Oct 18, 2026
b165ddf
Values (B9): an array of a union of records holds every kind's fields
jeany55 Oct 18, 2026
9581e07
Plan: B9 as built
jeany55 Oct 18, 2026
2e54adb
Generators finished, and async functions in libraries
jeany55 Oct 18, 2026
bb39cea
trigscript/cutscene, and what it needed of the language
jeany55 Oct 18, 2026
523bd85
Plan: generators finished, async functions in libraries, trigscript/c…
jeany55 Oct 18, 2026
eae2bcd
typeof narrowing in c ? a : b, and Date.now()/Math.random() refused i…
jeany55 Oct 18, 2026
8a6608f
Merge pull request #21 from scm-js/refs-values
jeany55 Oct 18, 2026
aa579d2
Plan: a second sweep, and what is left
jeany55 Oct 18, 2026
d25dc80
The second sweep's small gaps, and its fault
jeany55 Oct 19, 2026
e55af1c
Merge pull request #22 from scm-js/small-gaps
jeany55 Oct 19, 2026
2489953
Cost step 1: the weights probe, and what Remastered charges
jeany55 Oct 19, 2026
70bafa8
Cost step 1: the weights as played
jeany55 Oct 19, 2026
07cf6ba
Cost in Remastered's time: the weights in A4
jeany55 Oct 19, 2026
847b342
Merge pull request #23 from scm-js/frame-weights
jeany55 Oct 19, 2026
c99f404
The lowering bench: what a turn of each kind of statement costs Remas…
jeany55 Oct 19, 2026
ab4f47f
Cheaper lowering: adds in place, loop counters, ANDs in one trigger
jeany55 Oct 19, 2026
bf4dfda
Cheaper lowering: cells of their own, and a[i] -= v wraps
jeany55 Oct 19, 2026
bc3a847
Cheaper lowering: & and | with a constant as a masked write, stores c…
jeany55 Oct 19, 2026
1e5d945
Merge pull request #24 from scm-js/cheaper-lowering
jeany55 Oct 19, 2026
1c67d69
A view put together only when it changes: calls followed, and the ref…
jeany55 Oct 19, 2026
39df005
Merge pull request #25 from scm-js/view-calls
jeany55 Oct 19, 2026
5d08bbe
Screen: present() looks only at what was drawn; arrays of cells fille…
jeany55 Oct 19, 2026
aa2251d
Cheaper numbers: fixed products, quotients and roots, shifts, product…
jeany55 Oct 19, 2026
afb159c
Merge pull request #26 from scm-js/screen-present
jeany55 Oct 19, 2026
8515430
Merge pull request #27 from scm-js/fixed-math
jeany55 Oct 19, 2026
ff40ef0
A loop over units costs what the map holds: the table walked as the g…
jeany55 Oct 19, 2026
34c1653
Merge pull request #28 from scm-js/unit-lists
jeany55 Oct 19, 2026
1512caf
Plan: arrays at their cost — the bullets bench, what an array that gr…
jeany55 Oct 19, 2026
83df7ac
Arrays at their cost: growing arrays in cells of their own, an index …
jeany55 Oct 19, 2026
4875c78
The fast-cell probe: the neutral players' ore, gas and score, and Pla…
jeany55 Oct 19, 2026
52d185f
A division whose result is small, as comparisons: Math.min(H, Math.fl…
jeany55 Oct 19, 2026
dd62972
Fast cells played: tables outside the emulator 2.5x cheaper an action…
jeany55 Oct 19, 2026
45ac0d7
Merge pull request #29 from scm-js/cost-abstractions
jeany55 Oct 19, 2026
654bd83
Plan: the limits, and a backend of our own — two probes, value ranges…
jeany55 Oct 19, 2026
e0e1461
Two probes for the limits: a map that is not EUD (classic weights) an…
jeany55 Oct 19, 2026
85a84db
Value ranges: the values each number can take, and a lowering that ta…
jeany55 Oct 19, 2026
5a5c913
Backend, part 1: static routing — a variable read at one site and tou…
jeany55 Oct 19, 2026
83b5743
Backend, part 1b: routing set once in front of a loop, and copies for…
jeany55 Oct 19, 2026
7670971
Backend, part 2: record loops — reads and writes at a loop's counter …
jeany55 Oct 19, 2026
eb7b7eb
Plan: the backend's next steps; a loop's test turned round tried and …
jeany55 Oct 19, 2026
9570013
Plan: the two probes played — a classic map wins nothing; a native qu…
jeany55 Oct 19, 2026
ca08356
Probe: does Bring read a moved location's old counts in the same pass
jeany55 Oct 19, 2026
26e7a11
Plan: the stale-location probe played — writing a location's sides or…
jeany55 Oct 19, 2026
86a7788
A count after a location moves or units are made in it counts again: …
jeany55 Oct 19, 2026
e0f854d
Backend, part 3: fixed products and quotients in loops over cells of …
jeany55 Oct 19, 2026
08bd187
Images a script imports: .image files and a build-time Image
jeany55 Oct 19, 2026
bf82d2a
Halftone: greys in the fog, and a probe to play them
jeany55 Oct 19, 2026
78e6fc7
The emitter, E1: loops of numbers lowered to triggers of our own, eve…
jeany55 Oct 19, 2026
0e6c52c
Plan: the halftone probe as played
jeany55 Oct 19, 2026
824008e
Tile survey: each tile's minimap colour lit and in fog, for a recordi…
jeany55 Oct 19, 2026
c00c075
The emitter: islands — a statement it does not lower stays in a taken…
jeany55 Oct 19, 2026
a97d298
Plan: Remastered's minimap surveyed — snow, and fog at 0.64 of lit
jeany55 Oct 19, 2026
b3bc723
Fog maps on snow for Remastered graphics: BOARD=snow in the base map
jeany55 Oct 19, 2026
2053bf3
Plan: snow played in Remastered
jeany55 Oct 19, 2026
219f98e
The fog library, faster: brushes placed in pixels, a location's sides…
jeany55 Oct 19, 2026
f4339e3
Snow is the fog maps' board; the fog library says why
jeany55 Oct 19, 2026
055aaf6
Merge pull request #30 from scm-js/image-import
jeany55 Oct 19, 2026
90d96c2
Sound probe: timing, unlisted sounds, restart, channels, copies, loud…
jeany55 Oct 19, 2026
39267d3
Plan: the sound probe as played
jeany55 Oct 19, 2026
4728d24
The emitter, E3: more of a loop its own — calls inlined, cells of kno…
jeany55 Oct 19, 2026
0377596
Merge emitter-e3: more of a loop the emitter's own
jeany55 Oct 19, 2026
8828c19
Sounds a script imports or makes: .sound files and a build-time Clip
jeany55 Oct 19, 2026
d560f22
Sound demo: an import, a scale, a melody, drums, a chord, made at bui…
jeany55 Oct 19, 2026
711ae11
A tone ADPCM would make harsh goes in the map as 16-bit; square and s…
jeany55 Oct 19, 2026
f03c9a2
The emitter on by default: its probe played in Remastered prints what…
jeany55 Oct 19, 2026
51c331f
Plan: the emitter's heading back in place, and its probe played in Re…
jeany55 Oct 19, 2026
fb369e6
Merge trigscript-4 (images, halftone, snow) into backend-limits
jeany55 Oct 19, 2026
dd7510e
The standard library's "trigscript/music": instruments, patterns, songs
jeany55 Oct 19, 2026
5a410ef
Plan: the music demo as played
jeany55 Oct 19, 2026
9bf279b
Merge pull request #31 from scm-js/backend-limits
jeany55 Oct 19, 2026
f0ba2ca
Merge pull request #32 from scm-js/sound-probe
jeany55 Oct 19, 2026
59b22d6
Probe: what Remastered offers past eudplib — native registers, done t…
jeany55 Oct 19, 2026
4372cc2
The emitter, E4: stores into arrays, a counter's offsets as its place…
jeany55 Oct 19, 2026
d67ba48
A map of our own: no eudplib, no Python — our own layout after the st…
jeany55 Oct 19, 2026
fb748f4
Plan: the own-loader map played in Remastered — all eight lines ok
jeany55 Oct 19, 2026
5d1c18d
Plan: the own-loader map quits cleanly in Remastered
jeany55 Oct 19, 2026
8b06dd3
The emitter's own reciprocal, whole-number division in cells, each ac…
jeany55 Oct 19, 2026
1e3a4f2
The emitter: switches, loops the front end unrolled, booleans, cells …
jeany55 Oct 19, 2026
a396fb5
The emitter: shifts and copies after the Python side's statements by …
jeany55 Oct 19, 2026
50296fe
The emitter: a statement that reads a value computed ahead of the loo…
jeany55 Oct 19, 2026
3bd784d
Plan: the emitter's second round as built, with its numbers and what …
jeany55 Oct 19, 2026
d8c384e
The graph's scratch cells without an initial value: every read of one…
jeany55 Oct 19, 2026
3acb395
Merge emitter-e4
jeany55 Oct 19, 2026
44dc4c7
Merge primitives
jeany55 Oct 19, 2026
10309e1
Merge own-loader
jeany55 Oct 19, 2026
847ef70
Plan: the primitives probe as played — resources a sixth of an EUD ce…
jeany55 Oct 19, 2026
e089327
Plan: the emitter's second probe played in Remastered — stores and re…
jeany55 Oct 19, 2026
028396a
Merge pull request #33 from scm-js/beyond-eudplib
jeany55 Oct 19, 2026
2f56d81
Fixture maps' round trip: a text or a sound in a field the action doe…
jeany55 Oct 19, 2026
4b3010c
Merge pull request #34 from scm-js/wakka-roundtrip
jeany55 Oct 19, 2026
3084a6d
Plan: past eudplib — the machine the measurements describe, eighteen …
jeany55 Oct 19, 2026
75633e0
Calls in a map of our own: arguments into the callee's places, back b…
jeany55 Oct 19, 2026
e729e24
Calls cost less than eudplib's: results through their cells, copies r…
jeany55 Oct 19, 2026
e5dd335
Plan: calls on our own backend as built — the first measure, the thre…
jeany55 Oct 19, 2026
520ff0b
Plan: the own-calls probe as played in Remastered — every line ok, ro…
jeany55 Oct 19, 2026
0064bdb
Merge pull request #35 from scm-js/calls
jeany55 Oct 19, 2026
7eb4951
Ranges: the debug switch read only where there is a process
jeany55 Oct 19, 2026
eca3d5c
Sounds, step 1: two channels, a limiter, sinc resampling, Ogg Vorbis …
jeany55 Oct 19, 2026
993770e
Plan: the Korobeiniki A/B as played: the Ogg on time, whole, both sid…
jeany55 Oct 19, 2026
07168d6
Sounds, step 2: filters, drive, echo, plate reverb, chorus, compresso…
jeany55 Oct 19, 2026
5c83ab1
Plan: step 2 as played
jeany55 Oct 19, 2026
c9d7e5b
Music: songs of sections, chords by name, accents, swing, risers
jeany55 Oct 19, 2026
9d847e4
Music: import a MIDI file as notes, and music.midi makes a song of them
jeany55 Oct 19, 2026
d7d3eaa
Sound: the limiter keeps the wave between samples under its ceiling
jeany55 Oct 19, 2026
33d67c0
Plan: step 3 as played
jeany55 Oct 19, 2026
01f0547
Music: songs cut into bars and layers that the game steers bar by bar
jeany55 Oct 19, 2026
ead7eab
Sound: an Ogg goes in with two channels, and stingers fit under the bars
jeany55 Oct 20, 2026
41c873a
Music: stingers on the next beat, and songs cut every few beats
jeany55 Oct 20, 2026
8229c69
Sounds: the tools and the recipe to make the Korobeiniki work again
jeany55 Oct 20, 2026
02dec78
Tests: stems.playthrough is the pieces added up at their times
jeany55 Oct 20, 2026
dfb9dcf
Merge pull request #36 from scm-js/music-quality
jeany55 Oct 20, 2026
3a62c91
Recursion in a map of our own: a stack of cell triggers for each func…
jeany55 Oct 20, 2026
e5d8a2f
Plan: recursion on our own backend, as built and as played; the rando…
jeany55 Oct 20, 2026
e6dfa9c
Merge pull request #37 from scm-js/recursion
jeany55 Oct 20, 2026
3cd0064
Texts with numbers in them, in a map of our own: a template a print, …
jeany55 Oct 20, 2026
c599ffb
Plan: the own-print probe as played in Remastered — every line as the…
jeany55 Oct 20, 2026
e617cd7
Merge pull request #38 from scm-js/digits
jeany55 Oct 20, 2026
a6472f4
Arrays of a fixed length in maps of our own
jeany55 Oct 20, 2026
3d53581
Plan: the arrays probe played in Remastered
jeany55 Oct 20, 2026
1b8e1a8
Merge pull request #39 from scm-js/arrays
jeany55 Oct 20, 2026
490f3dc
A map of our own refuses what it built wrong without a word: a text f…
jeany55 Oct 20, 2026
7407c88
Plan: the way off eudplib, audited — what the nine steps left out, se…
jeany55 Oct 20, 2026
4e642bf
Merge pull request #40 from scm-js/off-eudplib-audit
jeany55 Oct 20, 2026
08cc73a
A map of our own says what it refuses: the part the emitter never tak…
jeany55 Oct 20, 2026
d87284f
A field read straight off a pick is the game's, not undefined when th…
jeany55 Oct 20, 2026
9b9c3da
The action order() compiles inside a program: it is not the event on.…
jeany55 Oct 20, 2026
de89495
music.loop(song) compiles: the wait at the end of each turn is the lo…
jeany55 Oct 20, 2026
6d3bf90
The ledger: a script for each feature, its step off eudplib, and what…
jeany55 Oct 20, 2026
90372f1
Plan: the ledger as built, by step, and what writing it found
jeany55 Oct 20, 2026
43b68a3
Merge pull request #41 from scm-js/ledger
jeany55 Oct 20, 2026
e4ab2db
The action order() gives orders.Move, orders.Patrol and orders.Attack…
jeany55 Oct 20, 2026
40d481d
A function that keeps a state is the program's, not worked out once w…
jeany55 Oct 20, 2026
569e549
Products, quotients and remainders in a map of our own: step 4, part 1
jeany55 Oct 20, 2026
e6cb5d1
Bits and shifts in a map of our own: step 4, part 2
jeany55 Oct 20, 2026
8f102d4
Own backend, numbers part 3 and 4: fixed point and random numbers
jeany55 Oct 20, 2026
b631877
Own backend, numbers part 5: square roots, sines, arc tangents, u8 an…
jeany55 Oct 20, 2026
9bdb136
Emitter tests: four heavy operations a random program, and five minutes
jeany55 Oct 20, 2026
9a0fa08
Merge pull request #42 from scm-js/wrong-builds
jeany55 Oct 20, 2026
f4d7394
Merge pull request #43 from scm-js/numbers
jeany55 Oct 20, 2026
d35a1c3
Own backend, the game's conditions and actions: step 5, part 1
jeany55 Oct 20, 2026
7ba9ac2
Own backend, fields from values and made texts: step 5, part 2
jeany55 Oct 20, 2026
a1d3e7c
Merge pull request #44 from scm-js/game-actions
jeany55 Oct 20, 2026
c8909e7
Own backend, the game read as a value: step 5, part 3
jeany55 Oct 20, 2026
ac002c0
Own backend: a game number compared with a value is the condition itself
jeany55 Oct 20, 2026
75bb069
Own backend, locations, players and latches: step 5, part 4
jeany55 Oct 20, 2026
2717d36
Own backend, prints at the centre of the screen: step 5 done but Wait
jeany55 Oct 20, 2026
70a910b
Merge pull request #45 from scm-js/game-reads
jeany55 Oct 20, 2026
8929ad0
Own backend, the game's tables: step 6
jeany55 Oct 20, 2026
c3532f0
Merge pull request #46 from scm-js/game-memory
jeany55 Oct 20, 2026
cab8a08
Own backend, programs, owners and players: step 7
jeany55 Oct 20, 2026
d2a1ad8
Step 7's test: the declaration's place, which the type-check wants
jeany55 Oct 20, 2026
1eb057e
Merge pull request #47 from scm-js/programs
jeany55 Oct 20, 2026
f188002
Own backend, texts known when built chosen while the game runs: step …
jeany55 Oct 20, 2026
1a901a1
Merge pull request #48 from scm-js/texts
jeany55 Oct 20, 2026
0f3f866
Own backend, texts made while the game runs: step 8, part 2
jeany55 Oct 20, 2026
7a8fb92
Own backend: triggers stacked in each other's room, as eudplib stacks…
jeany55 Oct 20, 2026
ce24898
Tests: the payload walker lays each graph out once
jeany55 Oct 20, 2026
8ba093e
Own backend, JSX, names and arrays of texts: step 8, part 3
jeany55 Oct 20, 2026
da38a48
Probe: each player's colour code beside every code, built both ways
jeany55 Oct 22, 2026
4767078
Merge pull request #49 from scm-js/made-texts
jeany55 Oct 22, 2026
86442e2
Merge pull request #50 from scm-js/text-size
jeany55 Oct 22, 2026
af627a7
Merge pull request #51 from scm-js/texts-part3
jeany55 Oct 22, 2026
e589412
Arrays written where they are used, and names and colours looked into
jeany55 Oct 22, 2026
fd74216
Merge pull request #52 from scm-js/language-gaps
jeany55 Oct 22, 2026
af249f4
A constant out of a u8 or u16 array's range is an error, as a variabl…
jeany55 Oct 22, 2026
1a7fd18
Own backend, arrays that grow and what stands on the heap: step 9
jeany55 Oct 22, 2026
b92bb7f
Plan: step 9's maps played in Remastered
jeany55 Oct 22, 2026
ba626b1
Own backend: remainders in place, copies laid out once, cells moved a…
jeany55 Oct 22, 2026
82233c9
Emitter: triggers joined in one walk, what functions name worked out …
jeany55 Oct 22, 2026
223a362
Merge branch 'growing-arrays' into emitter-follow-ups
jeany55 Oct 22, 2026
eedebed
Merge pull request #53 from scm-js/growing-arrays
jeany55 Oct 22, 2026
cbcd1e3
Merge pull request #54 from scm-js/emitter-follow-ups
jeany55 Oct 22, 2026
7bc3113
Own backend: units — values, fields, verbs, picks, loops, select()
jeany55 Oct 23, 2026
b1a137e
Plan: step 10's probe played in Remastered
jeany55 Oct 23, 2026
6ab8f11
Merge pull request #55 from scm-js/units
jeany55 Oct 23, 2026
b2ec26d
Own backend: errors — try, catch, finally, throw, uncaught, a stack r…
jeany55 Oct 23, 2026
5074416
Own backend: tasks, Promise.all and race — coroutines on the walk
jeany55 Oct 23, 2026
8b99fec
Plan: step 11's tasks probe played in Remastered
jeany55 Oct 23, 2026
3934934
Merge pull request #56 from scm-js/errors
jeany55 Oct 23, 2026
c22848b
Own backend: unit events — the map's events pass and on.*
jeany55 Oct 23, 2026
5884778
Plan: step 11's events probe played in Remastered
jeany55 Oct 23, 2026
d804368
Own backend: unit events by a watch of every unit place
jeany55 Oct 23, 2026
dd5c1a3
Merge pull request #57 from scm-js/events
jeany55 Oct 23, 2026
c188500
Plan: step 11's events watch played in Remastered
jeany55 Oct 23, 2026
c218fd5
Own backend, the players' input carried by the map itself: step 12, p…
jeany55 Oct 23, 2026
54bb003
Own backend, typed lines read where they were typed: step 12, part 2
jeany55 Oct 23, 2026
0f95884
Own backend input: a held key read up for one frame stays held; step …
jeany55 Oct 23, 2026
eeec5b1
Plan: step 12's re-test played in Remastered
jeany55 Oct 23, 2026
d227d15
Merge pull request #58 from scm-js/input
jeany55 Oct 23, 2026
80f32ab
Own backend, the map's own triggers as the game's nodes in the payloa…
jeany55 Oct 23, 2026
808c4ad
Own backend, the events watch gated: a quiet frame passes the unit pl…
jeany55 Oct 23, 2026
1b00816
Plan: step 13's probe played in Remastered
jeany55 Oct 23, 2026
61b7e1f
Plan: the gated events probe played in Remastered
jeany55 Oct 23, 2026
a001555
Merge pull request #59 from scm-js/triggers
jeany55 Oct 23, 2026
8b1a3a6
Step 14: the build in the editor — TrigScript builds the map itself
jeany55 Oct 23, 2026
e27cc62
Merge pull request #60 from scm-js/build
jeany55 Oct 23, 2026
bbbe7fc
Step 15: the tools on maps of our own
jeany55 Oct 23, 2026
675e21e
Merge pull request #61 from scm-js/tools
jeany55 Oct 23, 2026
c5afe2c
The Open list's bug pass: the emitter's masked fold, a program's own …
jeany55 Oct 23, 2026
9f3662e
Merge pull request #62 from scm-js/open-bugs
jeany55 Oct 23, 2026
b486662
The gate before the Python goes: random whole programs, every probe b…
jeany55 Oct 24, 2026
3bdb9c1
README: the language reference against the ledger, and a plainer read
jeany55 Oct 24, 2026
512bc5b
README: a link to the guide's eudplib comparison, and asides without …
jeany55 Oct 24, 2026
7eb9f31
The carriers' in-transit flag at byte 14 of the unit-properties recor…
jeany55 Oct 24, 2026
fd74beb
Every input id is the map's across runs: a second program hears the p…
jeany55 Oct 24, 2026
9eda549
A died unit's fields read as they are, and a task's lines shown to th…
jeany55 Oct 25, 2026
b3cc98c
Plan: the gate's pack played out, twelve of fourteen as their sheets ask
jeany55 Oct 25, 2026
715b8d1
Merge pull request #63 from scm-js/gate
jeany55 Oct 25, 2026
d25e975
Merge pull request #64 from scm-js/readme-language-4
jeany55 Oct 25, 2026
32599e3
The Python goes: TrigScript's own build is the only build (TrigScript…
jeany55 Oct 25, 2026
9bb9b2e
Step 17's leftovers: a printed number's field sized by its range, the…
jeany55 Oct 25, 2026
6ac43aa
Plan: Magenta on our backend — TrigScript is the library (decided 202…
jeany55 Oct 25, 2026
6a4e53d
Merge pull request #65 from scm-js/python-goes
jeany55 Oct 25, 2026
1d04316
TrigScript as the library: the trigscript.build service other plugins…
jeany55 Oct 25, 2026
23270cb
Plan: Magenta on our backend, part B built in plugin-magenta
jeany55 Oct 25, 2026
592b8e3
README: the build-service example is plugin code, not a script the RE…
jeany55 Oct 25, 2026
fceeadf
Merge pull request #66 from scm-js/magenta-library
jeany55 Oct 25, 2026
0aae2ad
bounds(location), and the unit fields Magenta's rows need (Magenta on…
jeany55 Oct 25, 2026
ac7f12b
Merge pull request #67 from scm-js/magenta-language
jeany55 Oct 25, 2026
9857162
Plan: Magenta on our backend, parts B and C merged, D built
jeany55 Oct 25, 2026
1ef2165
A unit's underAttack is its last event being a hit, not the alert timer
jeany55 Oct 25, 2026
4663c3c
buildTime: a building finished early keeps its hit points, and a Terr…
jeany55 Oct 25, 2026
91d2c5e
underAttack stays on the attack-notify timer, said for what it is: Re…
jeany55 Oct 25, 2026
c273bea
emitter test: a location's size read into consts, then its edges writ…
jeany55 Oct 26, 2026
7100034
Merge pull request #68 from scm-js/magenta-part-d-plan
jeany55 Oct 26, 2026
534fe3f
Merge pull request #69 from scm-js/under-attack
jeany55 Oct 26, 2026
6b5c572
TrigScript 4.0.0
jeany55 Oct 26, 2026
dc38a21
version.ts: 4.0.0
jeany55 Oct 26, 2026
a4cd80e
Merge pull request #70 from scm-js/release-4-0
jeany55 Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ jobs:
# `dist/testing.js` and its declarations are what another repository's tests import at
# a tag to run triggers, so they are held to the same rule.
with:
artifacts: dist/compiler.js dist/testing.js dist/testing.d.ts
artifacts: dist/compiler.js dist/testing.js dist/testing.d.ts dist/gametest.js
secrets: inherit
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ node_modules/
*.tsbuildinfo
# Blizzard's maps, copied in for the round-trip test; never committed.
fixtures/
# Videos a probe imports (probes/bad-apple.ts): not ours, made on the spot with scripts/import-video.mts.
probes/*.video
# The halftone probe's pictures and walks: made on the spot by probes/lib/halftone-gen.mts.
probes/*.image
# The sound demo's imported sound: made on the spot by probes/lib/sound-demo-gen.mts.
probes/*.sound
1,939 changes: 1,264 additions & 675 deletions README.md

Large diffs are not rendered by default.

88 changes: 78 additions & 10 deletions compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
* again: the script is never run on the main thread, where nothing could stop an endless
* loop in it.
*/
import type { CompileResult, ScriptFiles } from "./compiler/compiler";
import type { CompileResult, OwnCheck, ScriptFiles } from "./compiler/compiler";
import type { OwnBuildInput, OwnBuildOutcome } from "./compiler/backend/build";
import { ENTRY_URL } from "./compiler/entry";
import type { ScriptNames } from "./compiler/names";
import type { TestRunOptions } from "./compiler/testing";
Expand All @@ -41,6 +42,21 @@ export const libUrl = (dist: string = DEFAULT_DIST) => `${dist.replace(/\/+$/, "
export const compilerUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/compiler.js` : `${dist.replace(/\/+$/, "")}/compiler.js`;

/**
* What runs a script's tests beside the lifted game (`dist/gametest.js`, from `compiler/gameTestEntry.ts`), at this
* release's own tag like the compiler: its text is handed to the engine's page (TrigScript 4, A2).
*/
export const gametestUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/gametest.js` : `${dist.replace(/\/+$/, "")}/gametest.js`;

/** What Remastered is known to allow (`data/allowlist.json`, copied into `dist/` by the build), for Find EUD Error: fetched only then. */
export const allowlistUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/allowlist.json` : `${dist.replace(/\/+$/, "")}/allowlist.json`;

/** The Ogg Vorbis encoder (`compiler/vorbis.ts`, `dist/vorbis.wasm`), at this release's own tag: fetched only by a build that has a long clip to put in the map. */
export const vorbisUrl = (dist: string = DEFAULT_DIST) =>
dist === DEFAULT_DIST ? `https://cdn.jsdelivr.net/gh/scm-js/plugin-trigscript@v${VERSION}/dist/vorbis.wasm` : `${dist.replace(/\/+$/, "")}/vorbis.wasm`;

/**
* What the worker is told to import. The editor's loader turns a fetched plugin into
* `blob:` modules, and the compiler's own blob URL is the compiler the main thread would
Expand All @@ -51,23 +67,40 @@ export const compilerUrl = (dist: string = DEFAULT_DIST) =>
export const workerModuleUrl = (dist: string = DEFAULT_DIST) => (ENTRY_URL.startsWith("blob:") && !blobRefused ? ENTRY_URL : compilerUrl(dist));

export const COMPILE_TIMEOUT_MS = 15_000;
/** A build's compile makes the script's sounds too — a long song mixed and encoded — and gets longer. */
export const BUILD_TIMEOUT_MS = 60_000;

export interface CompileInput {
files: ScriptFiles;
names: ScriptNames;
/** Run the script's tests after the compile: the world they start from, and which of them. */
tests?: TestRunOptions;
/** Encode the sounds the map is to get (a build): a check leaves an Ogg Vorbis one as its name (`compiler/vorbis.ts`). */
sounds?: boolean;
/** Ask whether TrigScript's own build takes the programs for this map (`CompileResult.ownRefusal`). */
own?: OwnCheck;
/** Files that run as entries after `main.ts`, in order: what another plugin contributes to the build (`CompileOptions.entries`). */
entries?: readonly string[];
}

interface CompileRequest extends CompileInput {
id: number;
moduleUrl: string;
libUrl: string;
vorbisUrl?: string;
}

/** A map of our own built in the worker (`compiler/backend/build.ts`), off the main thread like a compile. */
interface BuildRequest {
id: number;
moduleUrl: string;
libUrl: string;
build: OwnBuildInput;
}

interface CompileResponse {
id: number;
result?: CompileResult;
result?: CompileResult | OwnBuildOutcome;
error?: string;
/** The worker could not load its compiler at all: every later request would fail the same way. */
fatal?: boolean;
Expand All @@ -78,19 +111,32 @@ importScripts(${JSON.stringify(TS_URL)});
let loading = null;
let lib = null;
self.onmessage = async (e) => {
const { id, moduleUrl, libUrl, files, names, tests } = e.data;
const { id, moduleUrl, libUrl, vorbisUrl, files, names, tests, own, entries, build } = e.data;
try {
if (!loading) loading = import(moduleUrl);
let mod;
try { mod = await loading; } catch (err) { loading = null; postMessage({ id, error: String((err && err.message) || err), fatal: true }); return; }
// The plugin fetched as one bundle is a blob too, but one that exports the plugin and not the compiler.
if (typeof mod.compileScript !== "function") { loading = null; postMessage({ id, error: "the module has no compiler in it", fatal: true }); return; }
// A build of a map of our own: the compile's programs into the scenario's bytes, no TypeScript needed.
if (build) {
if (typeof mod.buildOwnMap !== "function") throw new Error("This compiler bundle cannot build a map of its own: update the plugin.");
postMessage({ id, result: mod.buildOwnMap(build) });
return;
}
if (lib === null) {
const r = await fetch(libUrl);
if (!r.ok) throw new Error("Could not load the standard library from " + libUrl + " (" + r.status + ").");
lib = await r.text();
}
postMessage({ id, result: mod.compileScript(self.ts, files, names, { lib, tests }) });
// A check names the script's clips and makes none of them; a build makes them, the long ones encoded below.
const result = mod.compileScript(self.ts, files, names, { lib, tests, sounds: Boolean(vorbisUrl), own, entries });
// A build's long clips encoded as Ogg Vorbis; a check's left as names, their samples not sent.
if (typeof mod.finishSounds === "function") {
const encoder = vorbisUrl && result.sounds.some((s) => s.vorbis) ? await mod.loadVorbis(vorbisUrl) : null;
mod.finishSounds(result.sounds, encoder);
}
postMessage({ id, result });
} catch (err) {
postMessage({ id, error: String((err && err.message) || err) });
}
Expand All @@ -113,7 +159,8 @@ let worker: Worker | null = null;
let blobRefused = false;
let unavailable: { reason: string; at: number } | null = null;
let seq = 0;
const pending = new Map<number, { resolve: (r: CompileResult) => void; reject: (e: Error) => void; timer: ReturnType<typeof setTimeout> }>();
/** What is in flight: a compile, which a newer compile supersedes, or a build, which nothing does. */
const pending = new Map<number, { resolve: (r: CompileResult | OwnBuildOutcome) => void; reject: (e: Error) => void; timer: ReturnType<typeof setTimeout>; build: boolean }>();

/**
* The worker is a whole TypeScript instance (tens of MB of heap) and it is only busy while
Expand Down Expand Up @@ -188,14 +235,14 @@ function breakWorker(reason: string) {
}

/** The script ran too long: drop this worker (the next request starts another) and say so. */
function timeOut(id: number) {
function timeOut(id: number, ms: number) {
const p = pending.get(id);
if (!p) return;
pending.delete(id);
worker?.terminate();
worker = null;
rejectAll("The compile was stopped.");
p.reject(new Error(`The script did not finish in ${COMPILE_TIMEOUT_MS / 1000} seconds. Is there an endless loop outside program()?`));
p.reject(new Error(p.build ? `The build did not finish in ${ms / 1000} seconds.` : `The script did not finish in ${ms / 1000} seconds. Is there an endless loop outside program()?`));
}

/** The worker, started if there is none. Throws `CompilerUnavailable` when one cannot be made, or could not a moment ago. */
Expand Down Expand Up @@ -240,18 +287,39 @@ export function compileInBackground(input: CompileInput, dist: string = DEFAULT_
let w: Worker;
try { w = getWorker(); } catch (err) { return Promise.reject(err as Error); }
const id = ++seq;
// Anything still in flight is stale now.
// Any compile still in flight is stale now (a build is not: it answers whatever is typed meanwhile).
for (const [old, p] of pending) {
if (p.build) continue;
pending.delete(old);
clearTimeout(p.timer);
p.reject(new CompileSuperseded());
}
return new Promise<CompileResult>((resolve, reject) => {
pending.set(id, { resolve, reject, timer: setTimeout(() => timeOut(id), COMPILE_TIMEOUT_MS) });
const req: CompileRequest = { id, moduleUrl: workerModuleUrl(dist), libUrl: libUrl(dist), files: input.files, names: input.names, ...(input.tests ? { tests: input.tests } : {}) };
const ms = input.sounds ? BUILD_TIMEOUT_MS : COMPILE_TIMEOUT_MS;
pending.set(id, { resolve: (r) => resolve(r as CompileResult), reject, timer: setTimeout(() => timeOut(id, ms), ms), build: false });
const req: CompileRequest = { id, moduleUrl: workerModuleUrl(dist), libUrl: libUrl(dist), files: input.files, names: input.names, ...(input.tests ? { tests: input.tests } : {}), ...(input.sounds ? { vorbisUrl: vorbisUrl(dist) } : {}), ...(input.own ? { own: input.own } : {}), ...(input.entries ? { entries: input.entries } : {}) };
w.postMessage(req);
}).catch((err: Error) => {
if (err.message === RETRY) return compileInBackground(input, dist);
throw err;
});
}

/**
* Build a map of our own in the background (`compiler/backend/build.ts`): the compile's programs laid out into the
* scenario's bytes, which the plugin's build step hands the editor. Not superseded by a compile, and supersedes none.
*/
export function buildInBackground(input: OwnBuildInput, dist: string = DEFAULT_DIST): Promise<OwnBuildOutcome> {
let w: Worker;
try { w = getWorker(); } catch (err) { return Promise.reject(err as Error); }
const id = ++seq;
return new Promise<OwnBuildOutcome>((resolve, reject) => {
const ms = BUILD_TIMEOUT_MS;
pending.set(id, { resolve: (r) => resolve(r as OwnBuildOutcome), reject, timer: setTimeout(() => timeOut(id, ms), ms), build: true });
const req: BuildRequest = { id, moduleUrl: workerModuleUrl(dist), libUrl: libUrl(dist), build: input };
w.postMessage(req);
}).catch((err: Error) => {
if (err.message === RETRY) return buildInBackground(input, dist);
throw err;
});
}
Loading
Loading