An event says: when this happens, do that, once. Dusk comes, and the lamps start to fade up. A cap is clicked, and the balloon takes off. A visitor asks a guard a question, and the guard answers. In a program a function runs when another function calls it; in a world nothing calls the lantern. Its logic says what it is watching for and decides, when that happens, whether to act. The thing watched for is the trigger; what is done is the handler, an acting block.

How you write it

on(trigger, action)
on(trigger, { ...an acting block in Cone's form... })

on takes one action, or one acting block that does several, in order.

on(dusk, start(transition(lampLevel, to: 1, over: world(15 min), ease: linear, from: current, begin: dusk.due)))
on(dawn, start(transition(lampLevel, to: 0, over: world(15 min), ease: linear, from: current, begin: dawn.due)))

When the world's day reaches dusk, the lamp level fades in a straight line from wherever it is to 1 over a quarter of an hour of the world's day (15 s of simulation time when the day lasts 24 minutes, Values: the day length), counted from the moment dusk was due, not from when the message was read; a clock set forward past dusk finds the lamps already lit. A dusk while the lamps are on changes nothing.

Triggers

TriggerFires whenCarries
Messages from the browser's vocabulary
dusk, dawnThe world's day clock passes the hour the sky gives, sky.duskHour or sky.dawnHour (Sky and weather).The moment it was due: dusk.due.
clicked(part)Any visitor clicks the part: the click picks the nearest part its ray from the eye meets, at any distance, so a part hidden behind another cannot be clicked (the balloon's cap, behind the basket's walls, only from inside).The part.
entered(p), left(p)A being comes into or goes out of part p's area, declared by a call in the part's body, area(self, volume) (Physics: areas, one mechanism for triggers, zones, portal mouths and waking regions, proposal).The part; the being, written by: v (proposed).
createdThe part has just been made, at load or by a spawn (loading.html).—
hardLanding(b, speed:, cost:)A being lands hard (Physics: the built-in numbers).The being b, its landing speed and the cost to its health, bound by the handler: on(hardLanding(b, speed: s, cost: c), ...).
struck(p, by: q)The body p, asleep, is woken by a touch from a moving body q, however lightly; bodies asleep together with it wake and are struck too (Physics: a body's events, proposal).The body; the moving body that touched, by: q.
cameToRest(p)The body p falls asleep: by the engine's own stillness rule, or put to sleep with sleep(p).The body.
Actions by beings (visitors.html)
action(name, value, to: target, by: v)A being performs a named action: a visitor through their input, a villager through its mind.The value it carries, the part or place it is aimed at, the being that acted.
Messages from parts
message(name, from: p)Another part, or a world rule, sends the message (actions.html, send).The message's typed value; its sender.
An animator's own events (animators.html)
ended(h)The animator with handle h finishes, is stopped, or is superseded.—
marker(h, name)The animator passes a named moment, such as a clip's mark.—
Conditions
became(c)The condition c turns true.—
held(c, for: d)The condition has been true for the duration d without a break.—

A message is its own trigger. A world declares a message by sending it, and the message's name and its typed value are the trigger: send(zapLanded(on: t), to: world) raises it for the whole world, and on(zapLanded(on: b), ...) handles it, with b bound to the value it carried. The two are a pair. message(name, from: p) is the same trigger when the handler needs the sender.

The spoken word has its own trigger, spoke(speaker, words, to: v), which the world answers to show speech (visitors.html). Further engine messages, contact and covered, and a seat's seated, are proposed.

Conditions: became and held

A condition is a trigger. became(c) fires once, at the moment c turns true; held(c, for: d) fires once c has held that long. The engine watches a condition only when its inputs change, and fires only when the condition changes, never every tick. A condition held for a time is a small state machine (waiting, counting, fired) that the content should not have to write; held says it in one word.

// The looking-glass opens
each v in visitors {                                    // one watch for each visitor
  on(held(still(v) && inFront(v, of: glass, from: 0.6 m, to: 5.5 m, across: 2.5 m)
          && facing(v, toward: glass, within: 45.57 deg),
          for: sim(3 s)),
    set(open, true))
}
// ... and closes once nobody has been near for 10 s
on(held(open && !near, for: sim(10 s)), set(open, false))

A condition over beings is written each v in visitors around the rule, Cone's loop: it makes one rule for each visitor, and v is that visitor inside it.

Three conditions about a being are defined here, with the island's numbers:

There is no time-based while, for now. (Cone's while loop, a loop inside a block, is a different thing; that the two share a word is listed under Open questions.) "While X, do Y" is written as an entry and an exit, on(became(c), ...) and on(became(!c), ...), or as a subscription when Y is a value, as the lanterns' nearness is. A while that expands to the pair may be added once it is clear how far the pair goes: swimming while deep enough, a control usable while inside a circle of influence.

Handlers are acting blocks

What follows the trigger is an acting block. It may calculate first, under local names, and then act; its actions are done in order (blocks.html). A single action may follow the trigger directly, on(dusk, start(...)); anything more is a block in braces, with semicolons, and Cone's if, else if, else and match choose among actions.

// The guards' riddle
on(action(ask, q, to: g, by: v), {
  if within(v, g, 4 m) {
    if asked[v] {
      say(g, "You have had your question.", to: v);
    } else {
      set(asked[v], true);
      say(g, answer(g, q), to: v);
    }
  }
})

A visitor within 4 m asks one of four questions; the guard answers once, and to any second question says "You have had your question." A functional guard, if(condition, action, ...), has no else: this reply would need two guards that each tested asked[v], and their order would matter; a six-way reply written as a chain of guards could fire two branches. Cone's else and match remove that trap, and the functional guard is not used.

Calculate and kick off

"Calculate something and kick something off" needs no third kind of block. It is two lines: a derive that calculates, and an on(became(...)) watching it (subscriptions.html). The calculation stays pure; the rule fires once, however often the value is recalculated.

A guard against the default

Starting an animator on a value that another animator is writing replaces the older one, from the current value (animators.html). Where that is wrong, the handler says so with a guard. The balloon ignores a click during its ride; a being ignores a second hail while it is hailing:

on(clicked(cap), {
  start(keys(cap.offset, [key(sim(0 s), (0, -2 cm, 0)), key(sim(0.25 s), (0, 0, 0))], ease: steps(1)));
  if !riding {
    set(launched, now(sim));
    start(ride);
  }
})

Every click dips the cap 2 cm for a quarter of a second, even one the balloon ignores; a click during the dip starts a new one that takes over. Whether a guard is how every exception to "the newest takes over" is written is open.

What a handler returns

A response returns nothing by default. Two cases may want a returned value:

What it means

Who receives an event

When it runs

Events are handled first in each tick, before animators are stepped and subscriptions recomputed; they may start or stop animators. A part's on(created, ...) runs before any message to it is handled: a world that arrives after dusk sets its lamps on with its start act before it reads its first dusk or dawn, so the two can never race (Loading: start comes before every message). Messages sent while handling are, as recommended, delivered later in the same step in the order sent, with a limit on how many rounds a chain may take; the order between several receivers of one message is not to be relied on.

Who may change what

Where it runs

Where a handler runs follows from what it reads and writes: the riddle's handler reads a server-only field, so it runs on the server. Rules that write fields living on the server or on both run only on the server; reactions that only show (a smile, a wince) may run in each view. To begin with the server runs the world's logic. A rule inside a part with lives: client runs on each visitor's machine, and only there: it is how a sound or a flash is played in each view when the server has sent the message that asks for it (Sounds: a one-shot play).

Bounded running

Each block runs within a budget. Long work yields and resumes, so a runaway loop in one world cannot stall a server shared by everyone in it.

How it connects

A second trigger while one is running

A second trigger can be met in different ways: a dusk while the lamps are lit changes nothing; a click during the cap's dip holds the cap down longer; a click during the ride is ignored; a press during a hail is ignored; a spell during a spell replaces it, starting afresh.

Open questions

← Subscriptions | Contents | Actions →