Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Control Flow

ASYNC !! · CALL EVENT · CALL RENDER · CALL THREAD · CALL TIMER · DELAY · EVENT · FOR · SET YIELD · STOP · STOP RENDER · STOP THREAD · STOP TIMER · WITH LAYER · WITH ROOM · YIELD · cooldown · instate


ASYNC !!

Starts a call later, or again and again.

future = ASYNC function(arguments) !! modifier

Also documents: after, every

Parameters

  • after AS NUMBER = NULL
  • every AS NUMBER = NULL
  • unit AS STRING = NULL

After !!

  • after(time AS NUMBER, unit AS STRING = “milliseconds”) - Start the call once, this long from now.
  • every(time AS NUMBER, unit AS STRING = “milliseconds”) - Start the call again every so often, until it is stopped.

Examples

SCREEN 320, 240
ATOMIC ticks = 0
FUNCTION tick()
    ticks = ticks + 1
END FUNCTION
// Call tick every 20 milliseconds until it is stopped
t = ASYNC tick() !! every(20)
FOR i = 1 ... 2000
    IF ticks >= 3 THEN BREAK
    YIELD
END FOR
STOP t
PRINT "ticked", ticks, "times"
SCREEN 320, 240
FUNCTION shout(word)
    RETURN word + "!"
END FUNCTION
// Once, 50 milliseconds from now; the future becomes its answer
later = ASYNC shout("time's up") !! after(50)
PRINT RESOLVE(later)

Notes

With a modifier, I don’t start the call myself: I hand it to schedule, which starts it when the modifiers say. You still get a future straight away.

CALL EVENT

Call a function each time an event fires (event, bevent or the engine).

CALL EVENT callback, event
call_event(callback, event)

Also documents: args, guard, guard_fails, unsynced

Parameters

  • callback AS FUNCTION - The function to call.
  • event AS STRING - The event’s name.

After !!

  • args(value AS ARRAY) - Arguments it is always called with; without them it gets the event’s own.
  • guard(value AS ARRAY) - Where it registers and how it runs: a list of one or more of “host”, “player”, “client”, “single” (contexts) and “once” (never two at a time), even for just one.
  • guard_fails(on = TRUE) - A guard that does not hold is an error rather than skipped.
  • unsynced(on = TRUE) - Run it on the client only: a server-side interpreter skips it.

Example

SCREEN 320, 240
ATOMIC score = 0
FUNCTION on_coin(points)
    score = score + points
END FUNCTION
CALL EVENT on_coin, "coin"
EVENT "coin" !! args([10])
EVENT "coin" !! args([25])
FOR i = 1 ... 2000
    IF score >= 35 THEN BREAK
    YIELD
END FOR
PRINT "score", score

Notes

It’s also the function call_event, for when you want what it gives back.

CALL RENDER

Call a function once every frame, on the render tick, until stop_render.

CALL RENDER callback [, id]
call_render(callback [, id])

Also documents: args, guard, guard_fails, unsynced

Parameters

  • callback AS FUNCTION - The function to call.
  • id AS STRING = NULL - Its name, for stop_render.

After !!

  • args(value AS ARRAY) - Arguments it is called with.
  • guard(value AS ARRAY) - Where it registers and how it runs: a list of one or more of “host”, “player”, “client”, “single” (contexts) and “once” (never two at a time), even for just one.
  • guard_fails(on = TRUE) - A guard that does not hold is an error rather than skipped.
  • unsynced(on = TRUE) - Run it on the client only: a server-side interpreter skips it.

Example

SCREEN 320, 240
ATOMIC frames = 0
FUNCTION draw()
    frames = frames + 1
    CLS
    CIRCLE (160, 120), frames, 12
END FUNCTION
CALL RENDER draw, "scene"
FOR i = 1 ... 2000
    IF frames >= 3 THEN BREAK
    YIELD
END FOR
STOP RENDER "scene"
PRINT "drew", frames >= 3

Notes

It’s also the function call_render, for when you want what it gives back.

CALL THREAD

Run a function on a thread of its own.

CALL THREAD callback [, id]
call_thread(callback [, id])

Also documents: args, guard, guard_fails, unsynced

Parameters

  • callback AS FUNCTION - The function to run.
  • id AS STRING = NULL - Its name, for stop_thread.

After !!

  • args(value AS ARRAY) - Arguments it is called with.
  • guard(value AS ARRAY) - Where it registers and how it runs: a list of one or more of “host”, “player”, “client”, “single” (contexts) and “once” (never two at a time), even for just one.
  • guard_fails(on = TRUE) - A guard that does not hold is an error rather than skipped.
  • unsynced(on = TRUE) - Run it on the client only: a server-side interpreter skips it.

Example

SCREEN 320, 240
ATOMIC total = 0
FUNCTION add_up(n)
    FOR i = 1 ... n
        total = total + i
    END FOR
END FUNCTION
// add_up runs on a thread of its own while the main program waits
CALL THREAD add_up, "sum" !! args([100])
FOR i = 1 ... 2000
    IF total = 5050 THEN BREAK
    YIELD
END FOR
PRINT total

Notes

It’s also the function call_thread, for when you want what it gives back.

CALL TIMER

Call a function every interval (or once after it, with after).

CALL TIMER callback, interval [, unit, id]
call_timer(callback, interval [, unit, id])

Also documents: after, args, guard, guard_fails, unsynced

Parameters

  • callback AS FUNCTION - The function to call.
  • interval AS NUMBER - How long between calls.
  • unit AS STRING = NULL - “milliseconds”, “seconds” (the default) or “minutes”.
  • id AS STRING = NULL - Its name, for stop_timer.

After !!

  • after(on = TRUE) - Call it once, after the interval, not every interval.
  • args(value AS ARRAY) - Arguments it is called with.
  • guard(value AS ARRAY) - Where it registers and how it runs: a list of one or more of “host”, “player”, “client”, “single” (contexts) and “once” (never two at a time), even for just one.
  • guard_fails(on = TRUE) - A guard that does not hold is an error rather than skipped.
  • unsynced(on = TRUE) - Run it on the client only: a server-side interpreter skips it.

Examples

SCREEN 320, 240
ATOMIC ticks = 0
FUNCTION tick(n)
    ticks = ticks + n
END FUNCTION
// Every 20 milliseconds, called with 1 each time
CALL TIMER tick, 20, "milliseconds", "clock" !! args([1])
FOR i = 1 ... 2000
    IF ticks >= 3 THEN BREAK
    YIELD
END FOR
STOP TIMER "clock"
PRINT "ticked", ticks >= 3
SCREEN 320, 240
ATOMIC done = FALSE
FUNCTION ring()
    done = TRUE
END FUNCTION
// Once, after 30 milliseconds
CALL TIMER ring, 30, "milliseconds", "alarm" !! after
FOR i = 1 ... 2000
    IF done THEN BREAK
    YIELD
END FOR
PRINT "rang", done

Notes

It’s also the function call_timer, for when you want what it gives back.

DELAY

Wait ms milliseconds, or until an event fires (until, with an optional timeout).

DELAY [ms]
delay([ms])

Also documents: timeout, until

Parameters

  • ms AS NUMBER = NULL - How long.

After !!

  • timeout(value AS NUMBER) - Give up waiting for it after this many milliseconds.
  • until(value AS STRING) - Wait for the next firing of this event.

Examples

SCREEN 320, 240
start = timer
DELAY 50
PRINT "waited", timer >= start
SCREEN 320, 240
FUNCTION open_door()
    EVENT "door_open"
END FUNCTION
ASYNC open_door() !! after(20)
// Wait for the event, but no more than a second
DELAY !! until("door_open") timeout(1000)
PRINT "the door is open"

Notes

It’s also the function delay, for when you want what it gives back.

EVENT

Fire an event: here, or (in a CrashNet game) on the server, the host or the clients.

EVENT name
event(name)

Also documents: args, room, slot, target

Parameters

  • name AS STRING - The event.

After !!

  • args(value AS ARRAY) - Arguments its handlers are called with.
  • room(value AS STRING) - The room !! target("servers") or target("clients") names.
  • slot(value AS NUMBER) - The player slot target="server" or "client" names.
  • target(value AS STRING) - Where it fires: “server” (SERVER; with slot, SERVER n), “servers” (SERVERS; with room, SERVERS ROOM), “host”, “client” with slot, “clients” (CLIENTS; with room, CLIENTS ROOM). Omitted: here. In a single-player game every target fires here.

Example

SCREEN 320, 240
ATOMIC heard = ""
FUNCTION on_door(name)
    heard = name
END FUNCTION
CALL EVENT on_door, "door"
// Fire it here; in a CrashNet game !! target("clients") would send it to every client
EVENT "door" !! args(["opened"])
FOR i = 1 ... 2000
    IF heard <> "" THEN BREAK
    YIELD
END FOR
PRINT "the door was", heard

Notes

It’s also the function event, for when you want what it gives back.

FOR

Steps through an array, a range or a map. A range counts up or down, whichever way gets from its start to its end. With a step it moves in jumps of that size; the sign of the step does not matter.

FOR item = source [, step]
    ...
END FOR

Also documents: step

Parameters

  • source
  • step AS NUMBER = 1

After !!

  • step(size AS NUMBER) - How far FOR moves on each pass.

Example

SCREEN 320, 240
FOR n = 1 ... 5
    PRINT n
END FOR
FOR n = 10 ... 0 !! step(5)
    PRINT n
END FOR
FOR name = ["ada", "bob"]
    PRINT "hello", name
END FOR

Notes

Opens a block, and I run it once for each item each gives me. NEXT closes it too.

SET YIELD

Set the ceiling, in milliseconds, on how long a yield waits for the renderer.

SET YIELD ms
set_yield(ms)

Parameters

  • ms AS NUMBER - Milliseconds (default 16).

Example

SCREEN 320, 240
// Wait at most 8 milliseconds for the renderer at each YIELD
SET YIELD 8
FOR i = 1 ... 5
    YIELD
END FOR
PRINT "five quick yields"

Notes

It’s also the function set_yield, for when you want what it gives back.

STOP

Stops a timer or a thread, given the future its ASYNC line gave. Anyone waiting on it in RESOLVE gets NULL.

STOP future
stop_task(future)

Parameters

  • future AS FUTURE

Example

SCREEN 320, 240
ATOMIC beats = 0
FUNCTION beat()
    beats = beats + 1
END FUNCTION
t = ASYNC beat() !! every(20)
FOR i = 1 ... 2000
    IF beats >= 2 THEN BREAK
    YIELD
END FOR
STOP t
// Anyone waiting on a stopped timer gets NULL
PRINT RESOLVE(t) = NULL

Notes

It’s also the function stop_task, for when you want what it gives back.

STOP RENDER

Stop the render callback called id, or every one when id is omitted.

STOP RENDER [id]
stop_render([id])

Parameters

  • id AS STRING = NULL - Which one.

Example

SCREEN 320, 240
ATOMIC frames = 0
FUNCTION count_frames()
    frames = frames + 1
END FUNCTION
CALL RENDER count_frames, "counter"
FOR i = 1 ... 2000
    IF frames >= 2 THEN BREAK
    YIELD
END FOR
STOP RENDER "counter"
YIELD
held = frames
FOR i = 1 ... 5
    YIELD
END FOR
// No more calls once it is stopped
PRINT frames = held

Notes

It’s also the function stop_render, for when you want what it gives back.

STOP THREAD

Stop the thread called id, or every one when id is omitted.

STOP THREAD [id]
stop_thread([id])

Parameters

  • id AS STRING = NULL - Which one.

Example

SCREEN 320, 240
ATOMIC spins = 0
FUNCTION spinner()
    DO
        spins = spins + 1
        YIELD
    LOOP
END FUNCTION
CALL THREAD spinner, "spinner"
FOR i = 1 ... 2000
    IF spins >= 3 THEN BREAK
    YIELD
END FOR
STOP THREAD "spinner"
PRINT "stopped after", spins >= 3

Notes

It’s also the function stop_thread, for when you want what it gives back.

STOP TIMER

Stop the timer called id, or every one when id is omitted.

STOP TIMER [id]
stop_timer([id])

Parameters

  • id AS STRING = NULL - Which one.

Example

SCREEN 320, 240
ATOMIC ticks = 0
FUNCTION tick()
    ticks = ticks + 1
END FUNCTION
CALL TIMER tick, 10, "milliseconds", "fast"
FOR i = 1 ... 2000
    IF ticks >= 2 THEN BREAK
    YIELD
END FOR
STOP TIMER "fast"
YIELD
held = ticks
DELAY 50
PRINT ticks = held

Notes

It’s also the function stop_timer, for when you want what it gives back.

WITH LAYER

Draws on a layer for the length of the block, then goes back to the one before.

WITH LAYER layer
    ...
END WITH LAYER

Parameters

  • layer AS NUMBER

Example

SCREEN 320, 240
CREATE LAYER 1, 320, 240
WITH LAYER 1
    // Drawn on layer 1
    CIRCLE (160, 120), 40, 12
END WITH LAYER
// Back on the layer before
PSET (10, 10), 7
PRINT "drawn"

Notes

Opens a block I run once.

WITH ROOM

Marks what the block makes as belonging to a room.

WITH ROOM room
    ...
END WITH ROOM

Parameters

  • room AS STRING

Example

SCREEN 320, 240
LOADIMG "hero_run.png", "hero" !! spritesheet((40, 40))
// What this block makes belongs to the room "arena"
WITH ROOM "arena"
    ENABLE SPRITE 0
    SET SPRITE 0 !! x(100) y(100) image("hero") frame(0)
    SHOW SPRITE 0
END WITH ROOM
PRINT spritevisible(0)

Notes

Opens a block I run once.

YIELD

Let the engine run: wait until the renderer has taken what was sent.

YIELD
yield()

Example

SCREEN 320, 240
FOR x = 0 ... 30
    CLS
    CIRCLE (x * 10, 120), 8, 12
    // Let the engine draw this frame before the next
    YIELD
END FOR
PRINT "done"

Notes

It’s also the function yield, for when you want what it gives back.

cooldown

Whether it’s time again: TRUE the first time, then once every ticks, and FALSE in between. Each line it’s written on keeps its own time, unless it’s given a key.

cooldown(ticks AS NUMBER, resolution AS NUMBER = 60, key AS STRING = NULL)

Also documents: key

Parameters

  • ticks AS NUMBER - How long between, in ticks.
  • resolution AS NUMBER = 60 - How many ticks make a second.
  • key AS STRING = NULL

After !!

  • key(name AS STRING) - A name for the wait, so the places that give the same one share it.

Example

SCREEN 320, 240
fired = 0
FOR frame = 1 ... 120
    IF COOLDOWN(30) THEN fired = fired + 1
    YIELD
END FOR
PRINT "fired", fired, "times"

instate

Returns 1 if the current state should continue executing, 0 if the state needs to advance and the subroutine should willingly exit loops and return. Takes no arguments.

instate

Example

SCREEN 320, 240
FUNCTION idle()
    n = 0
    // Leave the loop when the state moves on
    WHILE instate AND n < 5
        n = n + 1
        YIELD
    END WHILE
    RETURN n
END FUNCTION
PRINT "idled", idle(), "frames"