Skip to content

Control flow

Compiles

Code runs top to bottom unless you tell it otherwise. Control flow is how you tell it otherwise.

let health: int = 40
if health <= 0 {
$debug.print_line("knocked out", null, false)
} else if health < 50 {
$debug.print_line("hurt", null, false)
} else {
$debug.print_line("fine", null, false)
}

The condition needs no parentheses. The braces are always required, even around one line.

return ends the function immediately. Returning at the top when there’s nothing to do (a guard clause) keeps the rest of the function about the normal case:

✗ Nested
event export fn created() {
let child: Entity = $entity.get_child_named("fx", true, $entity.get())
if child {
let fx: Component = $support.entity_get_component(child, "effect.vscript")
if fx {
$component.queue_event("play", fx, null, false)
}
}
}
✓ Guard clauses
event export fn created() {
let child: Entity = $entity.get_child_named("fx", true, $entity.get())
if !child {
return
}
let fx: Component = $support.entity_get_component(child, "effect.vscript")
if !fx {
return
}
$component.queue_event("play", fx, null, false)
}

Both work. The right one reads as a list of reasons to stop, followed by the thing it does.

switch picks one branch by comparing a value against several. _ catches everything else:

let round: int = 2
switch round {
1 => {
$debug.print_line("first round", null, false)
}
2 => {
$debug.print_line("second round", null, false)
}
_ => {
$debug.print_line("later round", null, false)
}
}

Each branch needs its braces. Only the matching branch runs; there’s no falling through to the next one.

for repeats a block once for each number in a range:

for i in 0..3 {
$debug.print_line("i is {}", [i], false) // 0, 1, 2
}

0..3 counts 0, 1, 2: up to but not including 3. Use ..= to include the end, and step to count in larger jumps:

for i in 1..=3 {
$debug.print_line("{}", [i], false) // 1, 2, 3
}
for i in 0..10 step 5 {
$debug.print_line("{}", [i], false) // 0, 5
}

The most common use is walking through a list by position:

let names: list = ["plaza", "garage", "roof"]
for i in 0..names.len() {
let name: string = names.get(i)
$debug.print_line("{}: {}", [i, name], false)
}

while repeats as long as its condition is true:

let mut at: Entity = $entity.get()
while at {
$debug.print_line("{}", [$entity.get_name(at)], false)
at = $entity.get_parent(at)
}

This climbs from the script’s entity to the top of the hierarchy, printing each name. Make sure something in the loop eventually makes the condition false. A loop that never ends freezes the game.

Inside for or while, break leaves the loop and continue skips to the next time round:

for i in 0..10 {
if i == 2 {
continue // skip 2
}
if i == 5 {
break // stop at 5
}
$debug.print_line("{}", [i], false) // 0, 1, 3, 4
}
For programmers

for i in a..b step n is the only for; a and b are expressions, n an integer literal. switch arms take any expression as the pattern and compare with equality; no ranges, guards or bindings. Everything is a statement except if … else … used as a value.