4.0 KiB
4.0 KiB
The script language
LlamaMod's script language (version 2) is a small structured language in the style of the
Script view. StratCompiler
compiles it to strat assembly, and
StratAssembler
turns that into bytecode with the same checks as hand-written assembly. It is used by the editor's
type editor (Language: Script) and by mods such as the arena. Complete reference:
notes/strat-language.md;
tests: StratCompilerTest.
A first strat
// bobs one cell up and down and turns
strat Bobber {
alias base = v0
alias phase = v1
setModel(model:5) // any opcode: name<inline operands>(stack arguments)
base = self.y + 1 // 1 = one cell
phase = 0
on touched { // event handler (type 4), runs as a subroutine
soundPlay(sound:6)
}
loop {
phase = phase + 4deg
self.y = base + sin(phase)
turnLeft(4deg)
yield // end of this frame
}
}
sub wobble { // called with "call wobble", ends with return
self.rotZ = sin(self.rotY) * 0.1
}
Building blocks
| Construct | Notes |
|---|---|
strat Name { … } |
a type; its label Name is the entry point |
sub name { … }, call name, return |
subroutines |
alias name = v3 |
a name for a variable; parameter names from the object schema are predefined |
x = e, x += e, -=, *=, /= |
assignment; x += 1 compiles to inc |
| places | vN, alias, gN, self.prop, player.prop, parent.prop, parent.vN, camera. / target. / boss. / dialog.prop |
if (c) { } else if (c) { } else { } |
|
while (c) { }, loop { }, do { } while (c) |
a yield as the last statement of a loop becomes the loop's frame-ending back jump |
switch (e) { case 1: … break default: … } |
C rules (fall through without break); case values are constants |
break, continue, yield, end, remove |
|
on touched { … }, on event(type[, param]) [slot n] { … } |
event handlers; touched needs collisionOn<…>() and collRadius(…) |
op<a, b>(e1, e2) |
any opcode by name: inline operands in <> (raw words), stack arguments in (); the count must match the opcode |
Numbers
10and10.0are ten cells (fixed point);1.5,90degas in assembly.- Raw integers:
215r(= 215/4096) or hex0x10. Values inside<…>are always raw. - Operators over constants are folded at compile time with the VM's own fixed-point rules:
2 * 3 + 0.5→6.5. use rawNumbersswitches to the version-1 rule (plain integers raw) – old scripts get it automatically.
Operators (low → high precedence)
|| · && · | · & · == != · < > <= >= · << >> · + - · * / · unary
- !. In conditions && / || short-circuit; in a value both sides are evaluated.
References
model:N, anim:N, unit:N[+off] or unit:Name (another custom type), sound:N, tex:N –
indices into the level the code is compiled against.
What the compiler does not do (yet)
- no
forloop, no typed variables (everything is a 32-bit word, fixed point by convention); spawntakes raw type fields (spawn<0, 16, 5, 0xC400, 3>(unit:…)) – you must know the values the spawned type needs;- the Script view does not show event handlers as
on … { }blocks.
Tips
- Look at an original type that does something similar first (Script panel) and copy its numbers – speeds, animation indices, sounds and type fields are level-specific.
- Keep each frame's work small and always reach a
yieldin loops; a loop without a yield freezes the game. - Test in the game early: the compiler checks the code's form and references, not whether the object behaves well.