scmJS docs

Variables and numbers

Variables and records#

Variables hold numbers, booleans, texts, units and players. let p = { lives: 3, gold: 0, alive: true } is a record: a variable per field (p.lives -= 1, if (p.alive)), nested records included. A declared type (let p: { n: u8 } = { n: 0 }, an interface, a type) is honoured. A record handed to a function reaches it by reference, and p = { lives: 3, gold: 0, alive: true } sets every field at once. Variables live in the game's memory while the map is played. They take nothing from the map: no death counters, no switches, no triggers in the list.

A number or a boolean may be undefined where TypeScript says so: what find gives when nothing fits, an optional parameter left out, let n: number | undefined. ??, !== undefined and ! read it. A player in a variable can be changed and compared, but the player of an action is still one the script knows.

program(() => {
  const hp = [30, 80, 55];
  const strong = hp.find((h) => h > 50);        // a number, or undefined
  if (strong !== undefined) print(`${strong}`); // 80
  let leader: Player = P1;
  if (hp[0] < 50) leader = P2;
  if (leader == P2) print("Player 2 leads");    // Player 2 leads
});

Whole numbers#

A number is whole unless a fraction is stored into it. A whole number is TypeScript's as far as 32 bits go: signed, from −2 147 483 648 to 2 147 483 647. a - b is below zero when b is larger, Math.abs, Math.min, Math.max and clamp(x, lo, hi) mean what they say, and a number below zero is printed with its minus sign. The one difference is at the ends: a number wraps there, as x | 0 does, where TypeScript's would go on.

Three more types hold a number that is never below zero:

Type Holds At its ends
number −2 147 483 648 … 2 147 483 647 wraps, as x | 0
u32 0 … 4 294 967 295 wraps, as x >>> 0
u16 0 … 65 535 stops: below zero is 0, above is 65 535
u8 0 … 255 stops: below zero is 0, above is 255

let lives: u8 = 3 stops at its ends after the whole sum, never between its parts, which is what lives and cooldowns want. A u32 is for bit masks, hashes and counts past two thousand million. Everything the game is asked (minerals(), deaths(), u.hp) is a number. A constant its variable cannot hold is an error: let mask = 0xffffffff wants let mask: u32.

A number and a u32 do not mix in arithmetic without saying which is meant. h + n is an error that names the two ways out, u32(n) and i32(h), which read the same 32 bits the other way and cost nothing. A constant that fits is either, so h * 31 + 7 needs nothing. A comparison between the two is exact.

Where the game takes nothing below zero (a u8 or u16, a unit's hit points, an action's amount or count, random(n)'s bound, a place on the map), a number below zero goes in as 0. u.hp -= 1000 kills, and createUnit(p, unit, n, at) with n at −3 makes none.

Arithmetic. + − * / % between variables and constants, the compound assignments, ++ and --, and the bitwise & | ^ ~ << >> >>> over the 32 bits. >> keeps the sign of a number and >>> never does, as in JavaScript. / of two whole numbers rounds towards zero (-7 / 2 is −3), and % takes the sign of what is divided (-7 % 2 is −1). Math.floor and the other rounding functions around a whole division change nothing. Dividing by a variable that is 0 in the game gives 0; dividing by a constant 0 is an error. a ** 2 and Math.pow(a, 3) take a power known when the script is built, and Math.sign(n) and Math.log2(n) (rounded down) work on whole numbers.

Numbers with a fraction#

A number holds a fraction once one is stored into it: let speed = 1.5, x += 0.25, const d = Math.cos(a). It is then a fixed number, with about four decimal places, from about −32 768 to 32 767. fixed(n) makes a whole number one, and a parameter or variable typed fixed is one from the start. u8, u16 and u32 stay whole, and a fraction stored into one is an error.

program(() => {
  let speed = 1.5;                              // a fraction stored into it: a fixed number
  speed += 0.25;
  const across = Math.hypot(30, 40);
  const half = 7 / fixed(2);                    // 7 / 2 alone is 3, as whole numbers divide
  print(`${speed} ${across} ${half} ${Math.floor(speed * 10)}`);   // 1.75 50 3.5 17
});

trigscript/math adds lerp, easing curves, distance, angleTo and Vec, a point of two fixed numbers (Math).

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