Files
llamamod/docs/strats/script-language.md

4.0 KiB
Raw Permalink Blame History

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

  • 10 and 10.0 are ten cells (fixed point); 1.5, 90deg as in assembly.
  • Raw integers: 215r (= 215/4096) or hex 0x10. 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 rawNumbers switches 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 for loop, no typed variables (everything is a 32-bit word, fixed point by convention);
  • spawn takes 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 yield in 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.