Function Scripting

This is an advanced reference. Most Loreline stories don't need custom functions at all. The Writer's Guide covers everything you need to write rich interactive fiction. This page is here for when you want to go further with computed values, complex logic, or custom utilities.

This page is a reference for the scripting syntax used inside Loreline functions. If you're new to Loreline, start with the Writer's Guide to learn about beats, choices, and state management first.

Functions in Loreline are defined with the function keyword, followed by a name, parameters in parentheses, and an indented body:

function greet(name, time)
  if time < 12
    return "Good morning, $name!"
  else
    return "Good afternoon, $name!"

The body uses a general-purpose scripting syntax covered in detail below.

Values and types

Loreline supports these value types:

// Numbers (integers and decimals)
var count = 42
var price = 3.99
var offset = -5

// Strings (double quotes)
var greeting = "Hello"
var name = "Alice"

// Booleans
var active = true
var hidden = false

// Null (represents "no value")
var result = null

// Arrays (ordered lists)
var items = [1, 2, 3]
var empty = []

// Objects (key-value maps)
var stats = { hp: 100, mp: 50 }
var blank = {}

Type coercion

Values are automatically converted in boolean context:

When using + with a string, the other value is converted to a string automatically:

var message = "Score: " + 42    // "Score: 42"

Use the built-in conversion functions for explicit conversion:

Variables

Use var to declare local variables inside a function:

function calculateTotal(price, quantity)
  var subtotal = price * quantity
  var tax = subtotal * 0.1
  return subtotal + tax

Variables declared with var are local to the function. If you omit var, you're reading or writing a state variable instead:

state
  score: 0

function addPoints(points)
  var bonus = points * 2  // local variable
  score += bonus          // modifies the state variable

A var with no initial value defaults to null:

var result    // result is null

Local variables shadow state variables of the same name:

state
  x: 10

function example()
  var x = 99    // local x, does not affect state
  return x      // returns 99

Operators

Arithmetic

var a = 10 + 3    // 13, addition
var b = 10 - 3    // 7, subtraction
var c = 10 * 3    // 30, multiplication
var d = 10 / 3    // 3.333..., division
var e = 10 % 3    // 1, remainder (modulo)

Comparison

x == y    // equal
x != y    // not equal
x < y     // less than
x > y     // greater than
x <= y    // less than or equal
x >= y    // greater than or equal

Logical

a && b     // true if both are true
a || b     // true if either is true
!a         // true if a is false

You can also write and and or instead of && and ||:

if sunny and warm
  return "Perfect weather!"

if raining or snowing
  return "Stay inside."

Assignment

x = 10      // assign
x += 5      // add and assign (x is now 15)
x -= 3      // subtract and assign
x *= 2      // multiply and assign
x /= 4      // divide and assign

Increment and decrement

x++    // add 1 to x (postfix)
++x    // add 1 to x (prefix)
x--    // subtract 1 from x
--x    // subtract 1 from x

String concatenation

The + operator joins strings. Non-string values are converted automatically:

var label = "Item #" + 3        // "Item #3"
var info = name + " (" + age + ")"

String interpolation

Inside strings, $ inserts a variable's value and ${} evaluates an expression:

function formatReceipt(item, price, qty)
  var total = price * qty
  return "$qty x $item = $$${total}"

Field access and function calls work directly after $:

var line = "$player.name has $array_length(inventory) items"

For anything more complex, use ${}:

var summary = "Total: ${price * quantity + tax}"
var note = "Next: ${items[currentIndex + 1]}"

Control flow

if / else

function describe(temperature)
  if temperature > 30
    return "hot"
  else if temperature > 20
    return "warm"
  else if temperature > 10
    return "cool"
  else
    return "cold"

Parentheses around the condition are optional:

if (x > 0 && y > 0)
  // both positive

for loops

Range loops iterate from a start value up to (but not including) an end value:

function sum(n)
  var total = 0
  for i in 0...n
    total += i
  return total

0...5 produces the values 0, 1, 2, 3, 4. Variables work too: start...end.

Array loops iterate over each element:

function findItem(inventory, target)
  for item in inventory
    if item == target
      return true
  return false

while loops

function countdown(n)
  var result = ""
  while n > 0
    result += "$n... "
    n -= 1
  return result + "Go!"

do-while loops

The body runs at least once, then repeats while the condition holds:

function rollUntilSix()
  var attempts = 0
  do
    var roll = random(1, 6)
    attempts += 1
  while roll != 6
  return attempts

break and continue

break exits a loop immediately. continue skips to the next iteration:

function firstNegative(numbers)
  for n in numbers
    if n >= 0
      continue
    return n
  return null
function sumUntilLimit(values, limit)
  var total = 0
  for v in values
    if total + v > limit
      break
    total += v
  return total

return

Exits the function and optionally returns a value. Without an explicit return, the function returns null:

function isEven(n)
  return n % 2 == 0
function doSomething()
  if !ready
    return    // returns null, exits early
  // ... rest of the function

switch / case

Match a value against multiple cases:

function drinkPrice(drink)
  switch drink
    case "espresso"
      return 3
    case "latte"
      return 5
    case "cappuccino"
      return 4
    default
      return 0

Working with arrays

Creating and accessing

var items = ["sword", "shield", "potion"]
var first = items[0]          // "sword"
items[2] = "elixir"           // replace "potion" with "elixir"
var count = array_length(items)  // 3

Method-style calls

Array functions can be called with dot notation. These are equivalent:

array_add(items, "bow")    // function style
items.add("bow")           // method style

Common operations:

items.add("arrow")               // append to end
items.prepend("helm")            // insert at beginning
var last = items.pop()           // remove and return last
var first = items.shift()        // remove and return first
items.remove("shield")           // remove first occurrence
var found = items.has("sword")   // true if present
var pos = items.index("sword")   // position, or -1

Sorting and joining:

var sorted = [3, 1, 2].sort()           // [1, 2, 3]
var text = ["a", "b", "c"].join(", ")   // "a, b, c"
items.reverse()                          // reverse in place

Calls can be chained:

var result = [3, 1, 2].sort().join(", ")   // "1, 2, 3"

See the Built-in Functions: Array for the complete list.

Working with objects

Creating and accessing

var player = { name: "Alex", hp: 100, level: 1 }
var name = player.name          // dot notation
var hp = player["hp"]           // bracket notation
player.level = 2                // modify a property

Bracket notation is useful when the key is dynamic:

var key = "hp"
var value = player[key]    // same as player.hp

Method-style calls

var keys = player.keys()         // ["name", "hp", "level"]
var exists = player.has("mp")    // false
player.set("mp", 50)            // add a new key
player.remove("mp")             // remove a key

See the Built-in Functions: Map for the complete list.

Putting it together

Here's a complete example that combines multiple features: a function that formats an inventory list with quantities and a total value:

state
  inventory: ["sword", "shield", "potion", "potion", "arrow", "arrow", "arrow"]
  prices: { sword: 50, shield: 35, potion: 10, arrow: 2 }

function inventorySummary()
  // Count occurrences of each item
  var counts = {}
  for item in inventory
    if counts.has(item)
      counts.set(item, counts.get(item) + 1)
    else
      counts.set(item, 1)

  // Build the summary
  var lines = []
  var total = 0
  for name in counts.keys()
    var qty = counts.get(name)
    var value = prices.get(name) * qty
    total += value
    lines.add("$qty x $name ($$${value})")

  return lines.join("\n") + "\nTotal: $$$total"

barista: Want to see what you're carrying?
$inventorySummary()

This produces:

1 x sword ($50)
1 x shield ($35)
2 x potion ($20)
3 x arrow ($6)
Total: $111