DelveTalkcome in

Inspecting kind/room · the kinds · the language · the modules

{name}

Go there its page the town

id
kind/room
called
{name}
kind chain
kind/room → record
owner
the town
made by
the town
version
21
pin
kolaj-hasar
state
57111 bytes

Its spells

  • makedelvetalk kind/room make / name: <text, 1 to 64 characters> / spween: <optional text, up to 16384 characters>
    its Bend

To cast them, come in.

Who may do what

What its law admits now, before the spell's own Bend has its say.

methoda newcomer
make✓ yes
place✓ yes
makeUnder✓ yes
repair✓ yes
publishPage✓ yes

Its law

  • host opener the hand that opened the world may amend any lawrequest.kind == 2 and request.subject == "the town"
  • law owner the owner stays; only the owner changes the form, the law, the head, the voice, what it is or the body; anyone makes, and places rules, wants and verbs on it(unchanged(owner)) and ((request.subject == new.owner) or ((((((((request.kind == 0) or (request.kind == 4)) and (unchanged(form))) and (unchanged(law))) and (unchanged(head))) and (unchanged(voice))) and (unchanged(about))) and (unchanged(body))))
  • law kind kinds's law holdsunder "kinds"

Its Bend

what it runs: its entry module 18 lines
layer over ./Kind.obend
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Form.obend as Form
import ./Rows.obend as Rows
type State = Super.State
form make:
  name: text 1..64
  spween: text 0..16384
def make(state: State, input: MakeInput, context: Abi.Context) -> Activity<Super.Done>:
  Super.making(state, Lists.List.cons({head: {name: "name", value: input.name}, tail: Lists.List.cons({head: {name: "spween", value: input.spween}, tail: Lists.List.nil()})}), "", context)
form place:
  name: text 1..64
  spween: text 0..16384
  id: text 1..64
def place(state: State, input: PlaceInput, context: Abi.Context) -> Activity<Super.Done>:
  Super.making(state, Lists.List.cons({head: {name: "name", value: input.name}, tail: Lists.List.cons({head: {name: "spween", value: input.spween}, tail: Lists.List.cons({head: {name: "id", value: input.id}, tail: Lists.List.nil()})})}), "", context)
the body this kind lays on what it makes 684 lines

Each thing of this kind runs this layer over what the kind is of.

layer over ./Record.obend
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Document.obend as Document
import ./Form.obend as Form
import ./Plan.obend as Plans
import ./World.obend as World
import ./Card.obend as Card
import ./Relation.obend as Relations
import ./Rows.obend as Rows
import ./Text.obend as Text
import ./Scenes.obend as Scenes
import ./Spween.obend as Spween
# The room's body, over RECORD (docs/METAPHYSICS.md §7.2, `kind/room`): who is in it, what lies
# in it, and, when it has passages, a scene its readers walk by their choices. The reader is
# always the turn's principal; no method takes one as an argument. A room's own law is the
# record's: its scene (start, passages, cooldown, requires) is its owner's, and everyone else
# changes it only by these spells.
#
# Entering needs the scene's `requires` and start passage's guard to hold (their outside clauses judged by the host, Scenes.obend) and, with a cooldown, that the reader left at
# least `cooldown` clock minutes ago (folkway 10); an entering is the room's attention, and tends it
# (a weather over the room rains while someone has been there that day). A choice offers itself only while its guard
# holds (the inside over the variables, the outside by the host with the reader as `request.subject`);
# taking it applies its effects to the variables in order, writes `chose` (the rules its casts are
# fire on it, under the room's owner) and moves the reader to its target, or out at END. `say`, `emote` and `whisper` reach those in the room (folkway 5); a
# thing lying here is taken and put down by its own call, while its taker is here. A ```spween
# block posted here makes a room of its own under this one, by the room's kind.
type State = Super.State
type Choice = Scenes.Choice
type Passage = Scenes.Passage
type Var = Scenes.Var
type Presence = Rows.Presence
type Mine = Rows.Mine
type Departure = Rows.Departure
type Done = Scenes.Done
def leftKey(l: Departure) -> {at: Nat, who: String}:
  {at: l.at, who: l.who}
def holds(relation: Relations.Relation<Plans.Reference>, reference: Plans.Reference) -> Bool:
  Relations.has(relation, {object: reference.object})
def here(context: Abi.Context, state: State) -> Lists.Maybe<Presence>:
  Rows.presentAs(state.presence, context.principal)
def isHere(context: Abi.Context, state: State) -> Bool:
  Rows.isPresent(state.presence, context.principal)
# A call reads the room with the calling turn's staged entry or departure applied.
def present(state: State, context: Abi.Context) -> Activity<Bool>:
  isHere(context, state)
# Principals with avatars index presence in the room's transaction; a principal without an
# avatar still enters and leaves. A failed index refuses the presence write with it.
sum Indexed:
  indexed: {}
def indexPresence(context: Abi.Context) -> Activity<Indexed>:
  match world.viewField::<Plans.Reference>({object: {world: context.world, object: context.principal}, field: "at"}):
    case viewed(_): indexAvatar(context)
    case denied(_): Indexed.indexed({})
    case refused(_): Indexed.indexed({})
def indexAvatar(context: Abi.Context) -> Activity<Indexed>:
  match world.call::<Bool>({object: {world: context.world, object: context.principal}, method: "locate", argument: Plans.nothing()}):
    case returned(r): if r.result then Indexed.indexed({}) else refuse("refused location: The avatar did not index presence.")
    case refused(r): refuse("refused {r.clause}: {r.reading}")
    case denied(_): refuse("refused location: The avatar did not admit its room.")
# The reader's own variables here (`my.<name>`, Record's `mine`), and what a guard reads for them: the room's and theirs.
def ownOf(who: String, state: State) -> Lists.List<Var>:
  Lists.map(Lists.filter(Relations.rows(state.mine), fn(m: Mine) -> Bool: m.who == who), fn(m: Mine) -> Var: {name: m.name, value: m.value})
def readerVars(who: String, state: State) -> Lists.List<Var>:
  Scenes.readerVars(state.vars, ownOf(who, state))
# This turn's draw (Scenes.turnDraw: its entry's height, principal and intent), and the one a reader's passage reads
# its text with: the turn's that brought them there, kept on their presence; for one not here, this turn's.
def turnDraw(context: Abi.Context) -> Nat:
  Scenes.turnDraw(context.height, context.principal, context.intent)
def readerDraw(context: Abi.Context, state: State) -> Nat:
  match here(context, state):
    case some(p): p.value.draw
    case none(_): turnDraw(context)
def passageAt(id: String, state: State) -> Lists.Maybe<Passage>:
  Lists.find(state.passages, fn(p: Passage) -> Bool: p.id == id)
# A passage's guard belongs to the destination, including one stitched after its incoming choices.
def passageGuard(id: String, state: State) -> Scenes.Guard:
  if id == Scenes.ending() then Lists.List.nil() else guardAt(passageAt(id, state))
def guardAt(found: Lists.Maybe<Passage>) -> Scenes.Guard:
  match found:
    case none(_): Lists.List.nil()
    case some(p): p.value.guard
def entryGuard(state: State) -> Scenes.Guard:
  Lists.concat(state.requires, passageGuard(state.start, state))
# A draw's destination is judged after drawing; its card offers the choice's own guard until then.
def choiceGuard(c: Choice, state: State) -> Scenes.Guard:
  match c.among:
    case nil(_): Lists.concat(c.guard, passageGuard(c.to, state))
    case cons(_): c.guard
def lastLeft(who: String, state: State) -> Lists.Maybe<Departure>:
  Lists.find(Relations.rows(state.left), fn(l: Departure) -> Bool: l.who == who)
def cooling(who: String, clock: Nat, state: State) -> Bool:
  match lastLeft(who, state):
    case some(l): clock < l.value.at + state.cooldown
    case none(_): false
def leftAt(who: String, state: State) -> Nat:
  match lastLeft(who, state):
    case some(l): l.value.at
    case none(_): 0n
# What a spell may do, judged purely before anything is written: a refusal names itself and
# writes nothing. A move carries the edits of its effects, one write each, in order, and the room's variables and the
# reader's own as they leave them.
sum Verdict:
  enter: {at: String}
  move: {me: Presence, choice: Choice, chose: String, vars: Lists.List<Edit>, after: Lists.List<Var>, own: Lists.List<Var>}
  leave: {me: Presence, chose: String, vars: Lists.List<Edit>, after: Lists.List<Var>, own: Lists.List<Var>}
  no: Card.Refusal
def no(clause: String, reading: String) -> Verdict:
  Verdict.no({clause: clause, reading: reading})
def enterVerdict(context: Abi.Context, state: State) -> Verdict:
  if lapsed(state, context) then met(context, state) else enterNow(context, state)
def enterNow(context: Abi.Context, state: State) -> Verdict:
  match here(context, state):
    case some(_): no("alreadyHere", "Already here.")
    case none(_): if Relations.count(state.presence) >= Super.presenceMax() then no("roomFull", "{natText(Super.presenceMax())} are already here.") else met(context, state)
# Presence lapses with the room's quiet (obend: ultimo92 "Here" in room/7 from 04:40 to 12:58, #1335 to #2122). A
# presence row keeps no time of its own, and the room's `tended` is the clock of its last attention: an entry, a
# choice, a word said aloud. A room untended for `quietMinutes` shows whoever it held as last here, not here; the
# next entry takes their rows off in its own journaled write, so the record says who left with the entry that found
# the room empty, and a replay derives the same. Never by a timer: nothing is written while nobody comes.
def quietMinutes() -> Nat:
  360n
def lapsed(state: State, context: Abi.Context) -> Bool:
  Relations.count(state.presence) > 0n && state.tended + quietMinutes() <= context.clock
def lapsing(rows: Lists.List<Presence>) -> Activity<Indexed>:
  match rows:
    case nil(_): Indexed.indexed({})
    case cons(c): lapsingOne(c.head.who, c.tail)
def lapsingOne(who: String, rest: Lists.List<Presence>) -> Activity<Indexed>:
  let written(_) = write {presence: retract {who: who}}
  lapsing(rest)
def met(context: Abi.Context, state: State) -> Verdict:
  match Scenes.failing(entryGuard(state), readerVars(context.principal, state)):
    case some(c): no("unmet", "The room requires {Scenes.clauseText(c.value)}.")
    case none(_): if cooling(context.principal, context.clock, state) then no("cooldown", "One who left enters again only after the cooldown: from clock {natText(leftAt(context.principal, state) + state.cooldown)}.") else roomy(context, state)
# `left` keeps Super.leftMax() departures, and one still cooling must never be dropped for a newer
# one: everyone here may leave, so an entry is refused while the cooling departures, the readers
# here and this one would pass the limit.
def roomy(context: Abi.Context, state: State) -> Verdict:
  let cooled = Lists.filter(Relations.rows(state.left), fn(l: Departure) -> Bool: context.clock < l.at + state.cooldown)
  if state.cooldown > 0n && Lists.length(cooled) + Relations.count(state.presence) + 1n > Super.leftMax() then crowded(cooled, state) else Verdict.enter({at: state.start})
def crowded(cooled: Lists.List<Departure>, state: State) -> Verdict:
  match cooled:
    case cons(c): no("crowded", "{natText(Lists.length(cooled))} left within the cooldown; enter again from clock {natText(c.head.at + state.cooldown)}.")
    case nil(_): no("crowded", "The room is full; enter when one leaves.")
def leaveVerdict(context: Abi.Context, state: State) -> Verdict:
  match here(context, state):
    case none(_): no("notHere", "Not here.")
    case some(p): Verdict.leave({me: p.value, chose: "", vars: Lists.List.nil(), after: state.vars, own: ownOf(p.value.who, state)})
def chooseVerdict(choice: String, context: Abi.Context, state: State) -> Verdict:
  match here(context, state):
    case none(_): no("notHere", "Enter the room first.")
    case some(p): standing(p.value, choice, state, turnDraw(context))
def standing(me: Presence, choice: String, state: State, draw: Nat) -> Verdict:
  match passageAt(me.at, state):
    case none(_): no("noPassage", "This room has no passage to choose from.")
    case some(ps): offered(me, Lists.find(ps.value.choices, fn(c: Choice) -> Bool: c.label == choice), Scenes.chosen(me.at, placeOf(ps.value.choices, choice, 1n)), choice, state, draw)
# The place of the choice so labelled among a passage's, from 1.
def placeOf(choices: Lists.List<Choice>, label: String, n: Nat) -> Nat:
  match choices:
    case nil(_): n
    case cons(c): if c.head.label == label then n else placeOf(c.tail, label, n + 1n)
def offered(me: Presence, found: Lists.Maybe<Choice>, chose: String, choice: String, state: State, draw: Nat) -> Verdict:
  match found:
    case none(_): no("notOffered", "{me.at} does not offer {choice}")
    case some(c): guarded(me, drawnTo(c.value, chose, draw), chose, state)
def guarded(me: Presence, c: Choice, chose: String, state: State) -> Verdict:
  match Scenes.failing(choiceGuard(c, state), readerVars(me.who, state)):
    case some(f): no("guarded", "{c.label} needs {Scenes.clauseText(f.value)}.")
    case none(_): arriving(me, c, chose, state)
# A choice that draws between targets goes to the one this turn's draw lands on for it (Scenes.drawFor its `chose`).
def drawnTo(c: Choice, chose: String, draw: Nat) -> Choice:
  match c.among:
    case nil(_): c
    case cons(_): extend(c, {to: Scenes.drawn(c.among, Scenes.drawFor(draw, chose)), among: Lists.List.nil()})
def arriving(me: Presence, c: Choice, chose: String, state: State) -> Verdict:
  if c.to == Scenes.ending() then effected(me, c, chose, effectsOf(c, me.who, state), true) else reached(me, c, chose, state)
def reached(me: Presence, c: Choice, chose: String, state: State) -> Verdict:
  match passageAt(c.to, state):
    case none(_): no("noPassage", "{c.to} is not in this room")
    case some(_): effected(me, c, chose, effectsOf(c, me.who, state), false)
def effected(me: Presence, c: Choice, chose: String, edits: Effects, ends: Bool) -> Verdict:
  match edits:
    case full(f): no("varsFull", f.reading)
    case edits(e): if ends then Verdict.leave({me: me, chose: chose, vars: e.edits, after: e.vars, own: e.own}) else Verdict.move({me: me, choice: c, chose: chose, vars: e.edits, after: e.vars, own: e.own})
# The outside of a guard, judged in the turn: each condition in the law's grammar by the host, of the
# room as it stands with the reader as `request.subject` (`world.holds`), and what the reader keeps or
# holds by the thing's own `keeper` and `holder`, in order; the first that fails, if any. A condition
# the host cannot read fails, with its reason.
sum Judged:
  held: {}
  failed: {clause: Scenes.Clause, why: String}
def judging(guard: Scenes.Guard, context: Abi.Context) -> Activity<Judged>:
  match guard:
    case nil(_): Judged.held({})
    case cons(c): judgedOne(c.head, c.tail, context)
def judgedOne(clause: Scenes.Clause, rest: Scenes.Guard, context: Abi.Context) -> Activity<Judged>:
  match clause.test:
    case law(_): lawJudged(clause, rest, context)
    case holding(_): thingJudged(clause, rest, true, context)
    case lacking(_): thingJudged(clause, rest, false, context)
    case _: judging(rest, context)
def lawJudged(clause: Scenes.Clause, rest: Scenes.Guard, context: Abi.Context) -> Activity<Judged>:
  match world.holds({object: Plans.self(context), when: clause.value}):
    case held(h): if h.value then judging(rest, context) else Judged.failed({clause: clause, why: ""})
    case refused(r): Judged.failed({clause: clause, why: r.reading})
def thingJudged(clause: Scenes.Clause, rest: Scenes.Guard, wanted: Bool, context: Abi.Context) -> Activity<Judged>:
  match hasThing(clause.key, clause.value, context):
    case yes(_): if wanted then judging(rest, context) else Judged.failed({clause: clause, why: ""})
    case no(_): if wanted then Judged.failed({clause: clause, why: ""}) else judging(rest, context)
# Whether the reader keeps a thing (its `keeper`) or holds it (its `holder`), as the thing says.
sum Had:
  yes: {}
  no: {}
def had(b: Bool) -> Had:
  if b then Had.yes({}) else Had.no({})
def hasThing(category: String, thing: String, context: Abi.Context) -> Activity<Had>:
  if category == "holds" then holdsThing(thing, context) else keepsThen(category == "has", thing, context)
def keepsThen(orHolds: Bool, thing: String, context: Abi.Context) -> Activity<Had>:
  match world.viewField::<String>({object: {world: context.world, object: thing}, field: "keeper"}):
    case viewed(v): if v.state == context.principal then Had.yes({}) else if orHolds then holdsThing(thing, context) else Had.no({})
    case _: if orHolds then holdsThing(thing, context) else Had.no({})
def holdsThing(thing: String, context: Abi.Context) -> Activity<Had>:
  match world.viewField::<Plans.Reference>({object: {world: context.world, object: thing}, field: "holder"}):
    case viewed(v): had(v.state.object == context.principal)
    case _: Had.no({})
def failedReading(c: Scenes.Clause, why: String) -> String:
  if why == "" then Scenes.clauseText(c) else "{Scenes.clauseText(c)} ({why})"
# A verdict whose choice (or, entering, the scene's requirements) has an outside, judged before anything
# is written: a clause that fails refuses it by name.
def outward(verdict: Verdict, context: Abi.Context, state: State) -> Activity<Verdict>:
  match verdict:
    case enter(_): outwardBy(verdict, Scenes.outside(entryGuard(state)), "unmet", "The room requires ", context)
    case move(m): outwardBy(verdict, Scenes.outside(choiceGuard(m.choice, state)), "guarded", "{m.choice.label} needs ", context)
    case leave(l): if l.chose == "" then verdict else outwardBy(verdict, Scenes.outside(choiceAt(l.me.at, l.chose, state).guard), "guarded", "{choiceAt(l.me.at, l.chose, state).label} needs ", context)
    case no(_): verdict
def outwardBy(verdict: Verdict, guard: Scenes.Guard, clause: String, lead: String, context: Abi.Context) -> Activity<Verdict>:
  match judging(guard, context):
    case held(_): verdict
    case failed(f): no(clause, "{lead}{failedReading(f.clause, f.why)}.")
# The choice a `chose` names in a passage.
def choiceAt(at: String, chose: String, state: State) -> Choice:
  match passageAt(at, state):
    case some(p): nthChoice(p.value.choices, chose, at, 1n)
    case none(_): noChoice()
def noChoice() -> Choice:
  {label: "", to: "", effects: Lists.List.nil(), guard: Lists.List.nil(), text: "", among: Lists.List.nil()}
def nthChoice(choices: Lists.List<Choice>, chose: String, at: String, n: Nat) -> Choice:
  match choices:
    case nil(_): noChoice()
    case cons(c): if Scenes.chosen(at, n) == chose then c.head else nthChoice(c.tail, chose, at, n + 1n)
# The edits a choice's effects make, in order, each against the variables as the ones before it leave them, and the
# room's variables and the reader's own as they stand after them all. The room keeps sixteen variables of its own,
# sixteen of each reader's, and Super.mineMax() of its readers' together: one more is refused, never dropped.
sum Edit:
  room: {entry: Plans.Entries<Var, Var>}
  own: {row: Mine}
sum Effects:
  edits: {edits: Lists.List<Edit>, vars: Lists.List<Var>, own: Lists.List<Var>}
  full: {reading: String}
def effectsOf(c: Choice, who: String, state: State) -> Effects:
  effects(c.effects, who, state.vars, ownOf(who, state), Relations.count(state.mine), Lists.List.nil())
def effects(list: Lists.List<Scenes.Effect>, who: String, vars: Lists.List<Var>, own: Lists.List<Var>, rows: Nat, edits: Lists.List<Edit>) -> Effects:
  match list:
    case nil(_): Effects.edits({edits: edits, vars: vars, own: own})
    case cons(c): if Scenes.varies(c.head) == false then effects(c.tail, who, vars, own, rows, edits) else if Scenes.isMine(c.head.key) then ownOn(c.head, Scenes.ownName(c.head.key), c.tail, who, vars, own, rows, edits, Lists.find(own, fn(v: Var) -> Bool: v.name == Scenes.ownName(c.head.key))) else effectOn(c.head, c.tail, who, vars, own, rows, edits, Lists.find(vars, fn(v: Var) -> Bool: v.name == c.head.key))
def effectOn(effect: Scenes.Effect, rest: Lists.List<Scenes.Effect>, who: String, vars: Lists.List<Var>, own: Lists.List<Var>, rows: Nat, edits: Lists.List<Edit>, found: Lists.Maybe<Var>) -> Effects:
  match found:
    case some(v): effects(rest, who, Lists.map(vars, fn(w: Var) -> Var: if w.name == effect.key then {name: w.name, value: Scenes.applied(effect, w.value)} else w), own, rows, Lists.append(edits, Edit.room({entry: Plans.Entries.amendItem({item: v.value, change: {name: effect.key, value: Scenes.applied(effect, v.value.value)}})})))
    case none(_): if Lists.length(vars) >= 16n then Effects.full({reading: "Sixteen variables are already set."}) else effects(rest, who, Lists.append(vars, {name: effect.key, value: Scenes.applied(effect, "")}), own, rows, Lists.append(edits, Edit.room({entry: Plans.Entries.append({item: {name: effect.key, value: Scenes.applied(effect, "")}})})))
def ownOn(effect: Scenes.Effect, name: String, rest: Lists.List<Scenes.Effect>, who: String, vars: Lists.List<Var>, own: Lists.List<Var>, rows: Nat, edits: Lists.List<Edit>, found: Lists.Maybe<Var>) -> Effects:
  match found:
    case some(v): effects(rest, who, vars, Lists.map(own, fn(w: Var) -> Var: if w.name == name then {name: name, value: Scenes.applied(effect, w.value)} else w), rows, Lists.append(edits, Edit.own({row: {who: who, name: name, value: Scenes.applied(effect, v.value.value)}})))
    case none(_): if Lists.length(own) >= 16n then Effects.full({reading: "Sixteen of your own variables are already set here."}) else if rows >= Super.mineMax() then Effects.full({reading: "This room keeps {natText(Super.mineMax())} of its readers' own variables, and holds that many."}) else effects(rest, who, vars, Lists.append(own, {name: name, value: Scenes.applied(effect, "")}), rows + 1n, Lists.append(edits, Edit.own({row: {who: who, name: name, value: Scenes.applied(effect, "")}})))
# The reader's own variables, as a choice left them, in the room's `mine`.
def ownAfter(mine: Relations.Relation<Mine>, who: String, own: Lists.List<Var>) -> Relations.Relation<Mine>:
  match own:
    case nil(_): mine
    case cons(c): ownAfter(Relations.upsert(mine, {who: who, name: c.head.name, value: c.head.value}, Rows.mineKey), who, c.tail)
# Performs a verdict: a refusal writes nothing and is answered by its clause (Card.refusing);
# otherwise the writes, in order, and the reader is shown the room as they leave it.
def done(verdict: Verdict, context: Abi.Context, state: State) -> Activity<Done>:
  match outward(verdict, context, state):
    case no(n): roomRefused(n, context)
    case enter(e): performed(Verdict.enter(e), context, state)
    case move(m): performed(Verdict.move(m), context, state)
    case leave(l): performed(Verdict.leave(l), context, state)
def performed(verdict: Verdict, context: Abi.Context, state: State) -> Activity<Done>:
  match verdict:
    case no(n): roomRefused(n, context)
    case enter(e): if lapsed(state, context) then quietEntry(e.at, state, context) else entering(e.at, extend(state, {presence: Relations.insert(state.presence, {who: context.principal, handle: context.handle, at: e.at, draw: turnDraw(context)}, Rows.presenceKey), tended: context.clock}), context)
    case move(m): moving(m.me, m.choice, m.chose, m.vars, extend(state, {presence: Relations.upsert(state.presence, {who: m.me.who, handle: m.me.handle, at: m.choice.to, draw: turnDraw(context)}, Rows.presenceKey), vars: m.after, mine: ownAfter(state.mine, m.me.who, m.own), chose: m.chose, tended: context.clock}), context)
    case leave(l): departing(l.me, l.chose, l.vars, extend(state, {presence: Relations.retract(state.presence, {who: l.me.who}, Rows.presenceKey), vars: l.after, mine: ownAfter(state.mine, l.me.who, l.own), chose: if l.chose == "" then state.chose else l.chose}), context, state)
def quietEntry(at: String, state: State, context: Abi.Context) -> Activity<Done>:
  match lapsing(Relations.rows(state.presence)):
    case indexed(_): entering(at, extend(state, {presence: Relations.insert(Relations.empty(), {who: context.principal, handle: context.handle, at: at, draw: turnDraw(context)}, Rows.presenceKey), tended: context.clock}), context)
def entering(at: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {presence: insert {who: context.principal, handle: context.handle, at: at, draw: turnDraw(context)}, tended: set context.clock}
  match indexPresence(context):
    case indexed(_): entryTold(at, after, context)
def entryTold(at: String, after: State, context: Abi.Context) -> Activity<Done>:
  match lastLeft(context.principal, after):
    case some(l): if l.value.entry == 0n then welcomed(at, after, Document.text(""), context) else away(at, after, l.value.entry, context)
    case none(_): welcomed(at, after, Document.text(""), context)
def welcomed(at: String, after: State, lead: Document.Document, context: Abi.Context) -> Activity<Done>:
  match seen(after, context):
    case seen(v): Card.tell(Done.entered({at: at}), Document.concat(lead, Super.render(v.state, context)))
# One who comes back is told first what happened here while they were away, since the entry they
# left at: the lines others wrote here, newest first, then what changed under the room (`world.changed`
# under its id: rooms carved in it, things made here), three lines at most.
def away(at: String, after: State, since: Nat, context: Abi.Context) -> Activity<Done>:
  match world.changed({since: since, prefix: "{context.object}/", after: ""}):
    case changed(c): welcomed(at, after, awayLines(after, since, c.items, context), context)
    case denied(_): welcomed(at, after, awayLines(after, since, Lists.List.nil(), context), context)
def awayLines(state: State, since: Nat, changes: Lists.List<World.Change>, context: Abi.Context) -> Document.Document:
  let written = Lists.map(Lists.filter(Lists.reverse(Relations.order(state.lines, fn(l: Rows.Line) -> Nat: l.n)), fn(l: Rows.Line) -> Bool: l.at > since && l.author != context.principal), fn(l: Rows.Line) -> String: "{Card.shown(l.author, l.handle, context)}: {clip(l.text)}\n")
  let made = Lists.map(changes, fn(c: World.Change) -> String: "{c.object.object} v{natText(c.version)} by {Card.handle(c.by)}\n")
  let rows = Lists.take(Lists.concat(written, made), 3n)
  if Lists.length(rows) == 0n then Document.text("") else Document.text("While you were away (since entry {natText(since)}):\n{textJoin(rows, \"\")}\n")
def clip(text: String) -> String:
  if textLength(text) > 100n then "{textTake(text, 99n)}…" else text
def moving(me: Presence, c: Choice, chose: String, vars: Lists.List<Edit>, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {presence: upsert {who: me.who, handle: me.handle, at: c.to, draw: turnDraw(context)}, chose: set chose, tended: set context.clock}
  varied(vars, Done.moved({to: c.to}), c.text, after, context)
# The variables' edits in order, then the reader is shown what taking the choice said (its `text`, "" for none) and the
# room as the turn leaves it.
def varied(vars: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  match vars:
    case nil(_): told(result, said, after, context)
    case cons(c): wroteVar(c.head, c.tail, result, said, after, context)
# What a choice said comes first: above the passage it led to, so the card's budget counts it with the passage, or
# alone above the room when it ended the visit.
def told(result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let shown = Spween.rendered(said, readerVars(context.principal, after), readerDraw(context, after))
  match seen(after, context):
    case seen(v): Card.tell(result, if shown == "" then Super.render(v.state, context) else if isHere(context, after) then Super.render(saying(said, v.state, context), context) else Document.concat(Document.text("{shown}\n\n"), Super.render(v.state, context)))
def saying(said: String, state: State, context: Abi.Context) -> State:
  match here(context, state):
    case some(p): extend(state, {passages: Lists.map(state.passages, fn(q: Passage) -> Passage: if q.id == p.value.at then extend(q, {text: "{said}\n\n{q.text}"}) else q)})
    case none(_): state
# The room as the turn's reader sees it: at their passage, the choices whose outside holds, shown without
# it (a pure read of the card judges the inside alone and shows the outside beside each choice).
sum Seen:
  seen: {state: State}
def seen(state: State, context: Abi.Context) -> Activity<Seen>:
  match here(context, state):
    case none(_): Seen.seen({state: state})
    case some(p): seenAt(passageAt(p.value.at, state), state, context)
def seenAt(found: Lists.Maybe<Passage>, state: State, context: Abi.Context) -> Activity<Seen>:
  match found:
    case none(_): Seen.seen({state: state})
    case some(ps): seenSifted(ps.value, state, context)
def seenSifted(ps: Passage, state: State, context: Abi.Context) -> Activity<Seen>:
  match sifted(ps.choices, state, context):
    case sifted(k): Seen.seen({state: extend(state, {passages: Lists.map(state.passages, fn(p: Passage) -> Passage: extend(p, {guard: Lists.filter(p.guard, Scenes.inside), choices: if p.id == ps.id then k.choices else p.choices}))})})
sum Sifted:
  sifted: {choices: Lists.List<Choice>}
def sifted(choices: Lists.List<Choice>, state: State, context: Abi.Context) -> Activity<Sifted>:
  match choices:
    case nil(_): Sifted.sifted({choices: Lists.List.nil()})
    case cons(c): siftedOne(c.head, c.tail, state, context)
def siftedOne(c: Choice, rest: Lists.List<Choice>, state: State, context: Abi.Context) -> Activity<Sifted>:
  match judging(Scenes.outside(choiceGuard(c, state)), context):
    case held(_): keptBefore(extend(c, {guard: Lists.filter(c.guard, Scenes.inside)}), rest, state, context)
    case failed(_): sifted(rest, state, context)
def keptBefore(c: Choice, rest: Lists.List<Choice>, state: State, context: Abi.Context) -> Activity<Sifted>:
  match sifted(rest, state, context):
    case sifted(r): Sifted.sifted({choices: Lists.List.cons({head: c, tail: r.choices})})
def wroteVar(edit: Edit, rest: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  match edit:
    case room(r): wroteRoom(r.entry, rest, result, said, after, context)
    case own(o): wroteOwn(o.row, rest, result, said, after, context)
def wroteRoom(entry: Plans.Entries<Var, Var>, rest: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {vars: entry}))
  varied(rest, result, said, after, context)
def wroteOwn(row: Mine, rest: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {mine: upsert row}
  varied(rest, result, said, after, context)
# Leaving (or reaching END) records when, for the cooldown: the reader's last departure is
# retracted and this one inserted.
def departing(me: Presence, chose: String, vars: Lists.List<Edit>, after: State, context: Abi.Context, state: State) -> Activity<Done>:
  let written(_) = write {presence: retract {who: me.who}, chose: set if chose == "" then state.chose else chose}
  match indexPresence(context):
    case indexed(_): departureRecorded(me, chose, vars, after, context, state)
def departureRecorded(me: Presence, chose: String, vars: Lists.List<Edit>, after: State, context: Abi.Context, state: State) -> Activity<Done>:
  match lastLeft(me.who, state):
    case some(l): forgotten(l.value, vars, choiceAt(me.at, chose, state).text, after, context)
    case none(_): recorded(vars, choiceAt(me.at, chose, state).text, after, context)
def forgotten(last: Departure, vars: Lists.List<Edit>, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {left: retract leftKey(last)}
  recorded(vars, said, after, context)
def recorded(vars: Lists.List<Edit>, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {left: insert {at: context.clock, who: context.principal, entry: context.height}}
  varied(vars, Done.left({}), said, after, context)
form enter:
form choose:
  choice: text 1..64
form leave:
form say:
  line: text 1..280
form emote:
  line: text 1..280
form whisper:
  to: text 1..160
  line: text 1..280
form write:
  text: text 1..280
form chime:
form lines:
  from: natural 0..1000000000
form door:
  label: text 1..32
  to: text 1..128
form undoor:
  label: text 1..32
# The scene, replaced or extended by its owner: `rescene` replaces it whole in one turn, read as making reads it
# (Spween.parse: every target answered); the station stitches a wing a turn (Spween.wing) and restitches from its first.
# Each outside guard is read once by the host, so one it cannot read refuses by name; the casts are rules of the room's
# text, the old scene's removed and the new one's added (`stagedLawWithout`, the owner's other clauses kept), then the
# passages and requirements are written, and after the last wing a reader at a passage no longer there stands at the start.
# Variables, lines, presence and what lies here stay. `mark`, when not "", is kept as the wing's mark.
record Staging:
  text: String
  replace: Bool
  finish: Bool
  mark: String
form rescene:
  spween: text 1..16384
def rescene(state: State, input: ResceneInput, context: Abi.Context) -> Activity<Done>:
  if context.principal != state.owner then roomRefused({clause: "notOwner", reading: "Only the room's owner replaces its scene; a ```spween block posted here makes a room of your own under it."}, context) else staging(Spween.parse(input.spween), {text: input.spween, replace: true, finish: true, mark: ""}, state, context)
def staging(parsed: Spween.Parsed, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  match parsed:
    case refused(r): roomRefused({clause: "spween", reading: r.reason}, context)
    case parsed(p): if Spween.joined(if work.replace then Lists.List.nil() else state.passages, p.seed.passages) != "" then roomRefused({clause: "spween", reading: Spween.joined(if work.replace then Lists.List.nil() else state.passages, p.seed.passages)}, context) else stageGuards(p.seed, Scenes.conditions(p.seed), work, state, context)
def stageGuards(wing: Scenes.Seed, conditions: Lists.List<String>, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  match conditions:
    case nil(_): stageRules(wing, work, state, context)
    case cons(c): stageGuard(wing, c.head, c.tail, work, state, context)
def stageGuard(wing: Scenes.Seed, condition: String, rest: Lists.List<String>, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  match world.holds({object: Plans.self(context), when: condition}):
    case held(_): stageGuards(wing, rest, work, state, context)
    case refused(r): roomRefused({clause: "spween", reading: "The guard `{condition}` does not read: {r.reading}"}, context)
def stageRules(wing: Scenes.Seed, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  let all = Lists.concat(if work.replace then Lists.List.nil() else state.passages, wing.passages)
  match world.inspect({object: Plans.self(context), source: false}):
    case inspected(i): stageAmend(wing, work, all, "{if work.replace then stagedLawWithout(i.law, Scenes.castsOf(state.passages)) else i.law}{Scenes.ruleText(wing.passages)}", state, context)
    case _: roomRefused({clause: "spween", reading: "The room's text is not readable here; its scene stands as it was."}, context)
# Scene rules have the names of their passage, choice and cast. Only those rule lines are removed;
# the owner's other laws, rules and wants retain their text, wherever they stand in the law.
def stagedLawWithout(law: String, casts: Lists.List<Scenes.Cast>) -> String:
  let at = Text.indexOf(law, "\n")
  let line = textTake(law, at)
  let kept = if Lists.any(casts, fn(c: Scenes.Cast) -> Bool: Text.starts(line, "rule {c.id}_{natText(c.n)}{Scenes.suffix(c.k)} ") || Text.starts(line, "rule {c.id}_{natText(c.n)}{Scenes.suffix(c.k)}:")) then "" else line
  if at == textLength(law) then kept else stagedJoined(kept, stagedLawWithout(textDrop(law, at + 1n), casts))
def stagedJoined(first: String, rest: String) -> String:
  if first == "" then rest else if rest == "" then first else "{first}\n{rest}"
def stageAmend(wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, law: String, state: State, context: Abi.Context) -> Activity<Done>:
  match world.amend({object: Plans.self(context), law: law}):
    case amended(_): stageCleared(if work.replace then state.passages else Lists.List.nil(), if work.replace then state.requires else Lists.List.nil(), wing, work, all, state, context)
    case refused(r): roomRefused({clause: r.clause, reading: "The scene's casts are rules the room's text could not take: {r.reading}"}, context)
def stageCleared(passages: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  match passages:
    case nil(_): stageRequirementsCleared(requires, wing, work, all, state, context)
    case cons(c): stagePassageCleared(c.head, c.tail, requires, wing, work, all, state, context)
def stagePassageCleared(passage: Scenes.Passage, rest: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {passages: Plans.Entries.removeItem({item: passage})}))
  stageCleared(rest, requires, wing, work, all, state, context)
def stageRequirementsCleared(requires: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  match requires:
    case nil(_): stageWrite(wing, work, all, state, context)
    case cons(c): stageRequirementCleared(c.head, c.tail, wing, work, all, state, context)
def stageRequirementCleared(clause: Scenes.Clause, rest: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {requires: Plans.Entries.removeItem({item: clause})}))
  stageRequirementsCleared(rest, wing, work, all, state, context)
def stageWrite(wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  let first = work.replace || Lists.length(state.passages) == 0n
  let start = if first then wing.start else state.start
  let written(_) = write {start: set start, cooldown: set if first then wing.cooldown else state.cooldown}
  stageMarked(work, wing, all, if first then wing.requires else Lists.List.nil(), if work.finish then Relations.rows(state.presence) else Lists.List.nil(), start, context)
# A wing's mark, when its maker keeps one (the station's chained digest); a room's rescene keeps none.
def stageMarked(work: Staging, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, requires: Scenes.Guard, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  if work.mark == "" then stagePassages(wing.passages, requires, wing, all, presence, start, context) else stageStamped(work.mark, wing, all, requires, presence, start, context)
def stageStamped(mark: String, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, requires: Scenes.Guard, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {marks: insert {name: mark, who: context.principal, handle: context.handle, at: context.height}}
  stagePassages(wing.passages, requires, wing, all, presence, start, context)
# One write a passage and a clause, each appended, as the room writes its variables.
def stagePassages(passages: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  match passages:
    case nil(_): stageRequires(requires, wing, all, presence, start, context)
    case cons(c): stagePassage(c.head, c.tail, requires, wing, all, presence, start, context)
def stagePassage(passage: Scenes.Passage, rest: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {passages: Plans.Entries.append({item: passage})}))
  stagePassages(rest, requires, wing, all, presence, start, context)
def stageRequires(requires: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  match requires:
    case nil(_): stagePresence(presence, wing, all, start, context)
    case cons(c): stageRequire(c.head, c.tail, wing, all, presence, start, context)
def stageRequire(clause: Scenes.Clause, rest: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {requires: Plans.Entries.append({item: clause})}))
  stageRequires(rest, wing, all, presence, start, context)
def stagePresence(presence: Lists.List<Rows.Presence>, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, start: String, context: Abi.Context) -> Activity<Done>:
  match presence:
    case nil(_): Card.tell(Done.done({}), Document.text("Stitched {natText(Lists.length(wing.passages))} passages; the scene holds {natText(Lists.length(all))}. {stageWaiting(Spween.waiting(all))}\n"))
    case cons(c): if Lists.any(all, fn(p: Scenes.Passage) -> Bool: p.id == c.head.at) then stagePresence(c.tail, wing, all, start, context) else stageMoved(c.head, c.tail, wing, all, start, context)
def stageMoved(me: Rows.Presence, rest: Lists.List<Rows.Presence>, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {presence: upsert {who: me.who, handle: me.handle, at: start, draw: me.draw}}
  stagePresence(rest, wing, all, start, context)
def stageWaiting(waiting: Lists.List<String>) -> String:
  match waiting:
    case nil(_): "Every choice leads to a passage."
    case cons(_): "Still waiting for: {textJoin(waiting, \", \")}."
def enter(state: State, context: Abi.Context) -> Activity<Done>:
  done(enterVerdict(context, state), context, state)
def choose(state: State, input: ChooseInput, context: Abi.Context) -> Activity<Done>:
  done(chooseVerdict(input.choice, context, state), context, state)
def leave(state: State, context: Abi.Context) -> Activity<Done>:
  done(leaveVerdict(context, state), context, state)
# Talk reaches those in the room (in key order, at most Super.presenceMax()) under the speaker's
# handle; only someone here talks. What is said or done aloud is the room's line too, under the verb
# that said it, so one who comes later is told who spoke here and what was said; a whisper is not.
def say(state: State, input: SayInput, context: Abi.Context) -> Activity<Done>:
  if isHere(context, state) then aloud("say", input.line, "{speaker(context)}: {input.line}\n", state, context) else hushed("say", context)
def emote(state: State, input: EmoteInput, context: Abi.Context) -> Activity<Done>:
  if isHere(context, state) then aloud("emote", input.line, "* {speaker(context)} {input.line}\n", state, context) else hushed("emote", context)
def aloud(verb: String, text: String, line: String, state: State, context: Abi.Context) -> Activity<Done>:
  if Text.blank(text) then roomRefused({clause: "empty", reading: "A line needs words; this {verb} has none, so nothing was said"}, context) else spokenAloud(verb, text, line, state, context)
def spokenAloud(verb: String, text: String, line: String, state: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {lines: insert {author: context.principal, handle: context.handle, verb: verb, text: text, at: context.height, n: state.next}, next: add 1n, tended: set context.clock}
  spoken(Relations.rows(state.presence), line)
def whisper(state: State, input: WhisperInput, context: Abi.Context) -> Activity<Done>:
  if isHere(context, state) == false then hushed("whisper", context) else whispered(Lists.find(Relations.rows(state.presence), fn(p: Presence) -> Bool: p.who == input.to || p.handle == input.to), input.to, "{speaker(context)} whispers: {input.line}\n", context)
def whispered(found: Lists.Maybe<Presence>, to: String, line: String, context: Abi.Context) -> Activity<Done>:
  match found:
    case some(p): confirmed(p.value, line, context)
    case none(_): roomRefused({clause: "notHere", reading: "{to} is not here"}, context)
def confirmed(to: Presence, line: String, context: Abi.Context) -> Activity<Done>:
  match spoken(Lists.List.cons({head: to, tail: Lists.List.nil()}), line):
    case _: Card.tell(Done.done({}), Document.text("Whispered to {Card.shown(to.who, to.handle, context)}; no one else here hears it. Say to all: delvetalk {Card.called(context, \"room\")} say / line: <text>.\n"))
def hushed(doing: String, context: Abi.Context) -> Activity<Done>:
  roomRefused({clause: "notPresent", reading: "Only someone here can {doing}; enter first: delvetalk {Card.called(context, \"room\")} enter"}, context)
def roomRefused(refusal: Card.Refusal, context: Abi.Context) -> Activity<Done>:
  Card.refusing(Done.refused(refusal), refusal, context)
def speaker(context: Abi.Context) -> String:
  Card.name(context.principal, context)
def spoken(present: Lists.List<Presence>, line: String) -> Activity<Done>:
  match present:
    case nil(_): Done.done({})
    case cons(c): spokenTo(c.head, c.tail, line)
def spokenTo(to: Presence, rest: Lists.List<Presence>, line: String) -> Activity<Done>:
  match world.offer({to: to.who, document: Document.text(line)}):
    case _: spoken(rest, line)
def write(state: State, input: WriteInput, context: Abi.Context) -> Activity<Nat>:
  Super.write(state, {text: input.text}, context)
# Its bells rung together (RECORD's `ringTogether`): what lies here and what its doors open onto.
def chime(state: State, context: Abi.Context) -> Activity<Nat>:
  Super.ringTogether(state, context)
def lines(state: State, input: LinesInput, context: Abi.Context) -> Activity<Card.Replied>:
  Super.lines(state, {from: input.from}, context)
def door(state: State, input: DoorInput, context: Abi.Context) -> Activity<Card.Replied>:
  Super.door(state, {label: input.label, to: input.to}, context)
def undoor(state: State, input: UndoorInput, context: Abi.Context) -> Activity<Card.Replied>:
  Super.undoor(state, {label: input.label}, context)
# A thing takes itself off the floor into the hands of the principal, who must be here, and puts
# itself down where its holder stands, or where the room's owner lays it; only the thing itself
# (the calling object) does either. A thing lying here that is inspirited gives the room its line,
# under the one who inspirited it, and a mark, so the card lists it among the claimed.
def inspirited(state: State, input: {line: String}, context: Abi.Context) -> Activity<Done>:
  if context.caller == "" then roomRefused({clause: "notAThing", reading: "Only a thing is inspirited."}, context) else if holds(state.things, {world: context.world, object: context.caller}) == false then roomRefused({clause: "notLying", reading: "That is not lying here."}, context) else spirited(input.line, context, state)
def spirited(line: String, context: Abi.Context, state: State) -> Activity<Done>:
  let written(_) = write {lines: insert {author: context.principal, handle: context.handle, verb: "inspirit", text: if textLength(line) > 280n then "{textTake(line, 279n)}…" else line, at: context.height, n: state.next}, next: add 1n, marks: insert {name: claimMark(context.caller), who: context.principal, handle: context.handle, at: context.height}}
  Done.done({})
def claimMark(thing: String) -> String:
  "inspirited {thing}"
def claimed(thing: Plans.Reference, state: State) -> Bool:
  Super.marked(state, claimMark(thing.object))
def take(state: State, context: Abi.Context) -> Activity<Done>:
  if context.caller == "" then roomRefused({clause: "notAThing", reading: "Only a thing can take itself."}, context) else if isHere(context, state) == false then roomRefused({clause: "notPresent", reading: "Only someone here can take things."}, context) else if holds(state.things, {world: context.world, object: context.caller}) == false then roomRefused({clause: "notLying", reading: "That is not lying here."}, context) else lifted(context)
def lifted(context: Abi.Context) -> Activity<Done>:
  let written(_) = write {things: retract {object: context.caller}}
  Done.done({})
def put(state: State, context: Abi.Context) -> Activity<Done>:
  if context.caller == "" then roomRefused({clause: "notAThing", reading: "Only a thing can put itself down."}, context) else if isHere(context, state) == false && context.principal != state.owner then roomRefused({clause: "notPresent", reading: "Only someone here can put things down."}, context) else if holds(state.things, {world: context.world, object: context.caller}) then roomRefused({clause: "alreadyLying", reading: "That is already lying here."}, context) else laid(context)
def laid(context: Abi.Context) -> Activity<Done>:
  let written(_) = write {things: insert {world: context.world, object: context.caller}}
  Done.done({})
def methods() -> Lists.List<String>:
  Text.words("take put inspirited ~present")
def actions(state: State, context: Abi.Context) -> Lists.List<String>:
  Lists.concat(Text.words("enter choose leave say emote whisper write chime lines"), if context.principal == state.owner then Text.words("door undoor rescene") else Text.words(""))
# A ```spween block makes a room under this one, by this room's kind (its `makeUnder`, which reads
# the block); posted by this room's owner, it makes nothing and is answered with the spell that makes it this room's
# scene (`rescene`, the block under it). Anything else is the card's default.
def receive(state: State, input: Card.Reply, context: Abi.Context) -> Activity<Card.Replied>:
  match Spween.block(input.text):
    case some(b): if context.principal == state.owner then rescening(Spween.parse(b.value), context) else authored(b.value, Spween.parse(b.value), context, state)
    case none(_): answered(input, context, state)
def rescening(parsed: Spween.Parsed, context: Abi.Context) -> Activity<Card.Replied>:
  match parsed:
    case refused(r): Super.refused({clause: "spween", reading: r.reason}, context)
    case parsed(p): Card.tell(Card.Replied.usage({}), Document.text("This room is yours, so the block makes no room under it. It reads as a scene of {natText(Lists.length(p.seed.passages))} passages; to make it this room's scene, post it again with this line above it: delvetalk {Card.called(context, \"room\")} rescene\n"))
def answered(input: Card.Reply, context: Abi.Context, state: State) -> Activity<Card.Replied>:
  match seen(state, context):
    case seen(v): Card.answer(input, context, forms(), Super.render(v.state, context))
def authored(block: String, parsed: Spween.Parsed, context: Abi.Context, state: State) -> Activity<Card.Replied>:
  match parsed:
    case refused(r): Super.refused({clause: "spween", reading: r.reason}, context)
    case parsed(p): carving(if p.seed.title == "" then "a room" else p.seed.title, block, context, state)
def fieldOf(name: String, value: String) -> Rows.Field:
  {name: name, value: value}
def carvedFields(title: String, block: String) -> Lists.List<Rows.Field>:
  Lists.append(Lists.append(Lists.List.nil(), fieldOf("name", textTake(title, 64n))), fieldOf("spween", block))
def kindOf(state: State) -> String:
  if state.kind == "" then "kind/room" else state.kind
# A room makes rooms of its own kind (Record.making), each lying here.
def carving(title: String, block: String, context: Abi.Context, state: State) -> Activity<Card.Replied>:
  Super.making(extend(state, {kind: kindOf(state)}), kindOf(state), carvedFields(title, block), context, fn(object: String) -> Document.Document: carved(object, title), fn(m: Super.Made) -> Card.Replied: carvedAs(m))
def carved(object: String, title: String) -> Document.Document:
  Document.text("ROOM {title} is at {object}. Enter: delvetalk {object} enter\n")
def carvedAs(made: Super.Made) -> Card.Replied:
  match made:
    case made(_): Card.Replied.done({action: "create"})
    case refused(r): Card.Replied.refused(r)
# The room's head, over the record's lines and doors: its name and who is here; to a reader in
# it, their passage and the choices whose guards hold, then the variables; to anyone else, the
# spell that enters. What lies here follows.
def title(state: State) -> String:
  let name = if state.head == "" then "A ROOM" else state.head
  if textLength(name) > 120n then "{textTake(name, 119n)}…" else name
def choiceLine(c: Choice) -> String:
  match Scenes.outside(c.guard):
    case nil(_): "  * {c.label}\n"
    case cons(_): "  * {c.label} (if {textJoin(Lists.map(Scenes.outside(c.guard), Scenes.clauseText), \", \")})\n"
def varLine(v: Var) -> String:
  "{v.name} = {v.value}\n"
# At most eight of a list, then how many more.
def firstEight(lines: Lists.List<String>) -> String:
  let more = if Lists.length(lines) > 8n then "… and {natText(Lists.length(lines) - 8n)} more\n" else ""
  "{textJoin(Lists.take(lines, 8n), \"\")}{more}"
# What lies here, the unclaimed first with the spell that inspirits one, each line at most 400
# characters, eight things in all, then how many more.
def lyingLine(state: State) -> String:
  let all = Lists.take(Relations.rows(state.things), 8n)
  let open = Lists.filter(all, fn(t: Plans.Reference) -> Bool: claimed(t, state) == false)
  let kept = Lists.filter(all, fn(t: Plans.Reference) -> Bool: claimed(t, state))
  let more = if Relations.count(state.things) > 8n then "… and {natText(Relations.count(state.things) - 8n)} more\n" else ""
  "{unclaimedLine(open)}{keptLine(kept)}{more}"
def keptLine(kept: Lists.List<Plans.Reference>) -> String:
  if Lists.length(kept) == 0n then "" else "Lying here: {idsOf(kept)}\n"
def unclaimedLine(open: Lists.List<Plans.Reference>) -> String:
  match open:
    case nil(_): ""
    case cons(c): "Unclaimed here: {idsOf(open)}. Inspirit one: delvetalk {c.head.object} inspirit / surrounding: <1 to 400 characters>\n"
def idsOf(things: Lists.List<Plans.Reference>) -> String:
  textTake(textJoin(Lists.map(things, fn(t: Plans.Reference) -> String: t.object), ", "), 400n)
def presentLine(state: State, context: Abi.Context) -> String:
  if Relations.count(state.presence) == 0n then "" else "{if lapsed(state, context) then \"Last here, before it went quiet {natText((context.clock - state.tended) / 60n)} hours ago\" else \"Here\"}: {textJoin(Lists.map(Lists.take(Relations.rows(state.presence), 8n), fn(p: Presence) -> String: Card.shown(p.who, p.handle, context)), \", \")}{if Relations.count(state.presence) > 8n then \" and {natText(Relations.count(state.presence) - 8n)} more\" else \"\"}\n"
def headLine(state: State, context: Abi.Context, id: String) -> Document.Document:
  let count = "{title(state)} ({natText(if lapsed(state, context) then 0n else Relations.count(state.presence))} here)"
  let around = "{presentLine(state, context)}{lyingLine(state)}{if state.tone == \"\" then \"\" else Super.chimed(state, id)}"
  match here(context, state):
    case some(p): Document.text("{count}{passageText(p.value.at, state, context)}Say: delvetalk {id} say / line: <text>. Leave: delvetalk {id} leave.\n{around}")
    case none(_): Document.text("{count}. Enter: delvetalk {id} enter.\n{around}")
# The passage, its braces read for the reader (Spween.rendered), then the choices whose guards hold and the variables, as many as fit in what the
# card's room (Card.limit) leaves after the passage and the room's other lines (300 kept for them).
def passageText(at: String, state: State, context: Abi.Context) -> String:
  match passageAt(at, state):
    case none(_): ", you are here.\n"
    case some(ps): passageWith(ps.value, state, context, ", you are at {ps.value.id}:\n{Spween.rendered(ps.value.text, readerVars(context.principal, state), readerDraw(context, state))}\n{choicesHead(ps.value, state, context)}")
# A passage with no choice open keeps its reader; the owner of a scene of prose alone is shown the shape.
def choicesHead(ps: Passage, state: State, context: Abi.Context) -> String:
  match ps.choices:
    case cons(_): "Choices:\n"
    case nil(_): if context.principal == state.owner && Lists.length(state.passages) == 1n then "No choices: a reader stays here. A choice is * [Label] -> passage under the prose; the shape: delvetalk library read / page: spween; your scene replaced: delvetalk {Card.called(context, \"room\")} rescene\n" else "No choice is open here now; leave when you will.\n"
def passageWith(ps: Passage, state: State, context: Abi.Context, head: String) -> String:
  let choices = fitting(Lists.map(Lists.take(Lists.filter(ps.choices, fn(c: Choice) -> Bool: Scenes.holds(choiceGuard(c, state), readerVars(context.principal, state))), 8n), fn(c: Choice) -> String: choiceLine(extend(c, {guard: choiceGuard(c, state)}))), less(Card.limit(context), textLength(head) + 300n))
  let shown = Lists.concat(Lists.take(state.vars, 4n), Lists.take(Scenes.readerVars(Lists.List.nil(), ownOf(context.principal, state)), 4n))
  "{head}{choices}{fitting(Lists.map(shown, varLine), less(Card.limit(context), textLength(head) + textLength(choices) + 300n))}"
def less(room: Nat, used: Nat) -> Nat:
  if used >= room then 0n else room - used
def fitting(lines: Lists.List<String>, room: Nat) -> String:
  match lines:
    case nil(_): ""
    case cons(c): if textLength(c.head) > room then "" else "{c.head}{fitting(c.tail, room - textLength(c.head))}"

Amend it in the workshop

Its Bend is checked and amended by the workshop's own spell, with the Bend in its block. To send it, come in; the box is here then.

Its fields

owner
the town
ownerHandle
the town
name
room
of
record
form
name: text 1..64 (38 characters)
name: text 1..64
spween: text 0..16384
law
want company "a room nobody is in": count(new.presence) == 0 asks delvetalk {request.object} enter
head
{name}
voice
dusk-light and stone dust; a place where nobody has decided yet; there is no rush
about
a place walked by choices; prose alone is a scene: delvetalk kind/room make / name: the well / spwee (179 characters)
a place walked by choices; prose alone is a scene: delvetalk kind/room make / name: the well / spween: A well hums, {~ low | lower}. Choices: delvetalk library read / page: spween
body
layer over ./Record.obend (56116 characters)
layer over ./Record.obend
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Document.obend as Document
import ./Form.obend as Form
import ./Plan.obend as Plans
import ./World.obend as World
import ./Card.obend as Card
import ./Relation.obend as Relations
import ./Rows.obend as Rows
import ./Text.obend as Text
import ./Scenes.obend as Scenes
import ./Spween.obend as Spween
# The room's body, over RECORD (docs/METAPHYSICS.md §7.2, `kind/room`): who is in it, what lies
# in it, and, when it has passages, a scene its readers walk by their choices. The reader is
# always the turn's principal; no method takes one as an argument. A room's own law is the
# record's: its scene (start, passages, cooldown, requires) is its owner's, and everyone else
# changes it only by these spells.
#
# Entering needs the scene's `requires` and start passage's guard to hold (their outside clauses judged by the host, Scenes.obend) and, with a cooldown, that the reader left at
# least `cooldown` clock minutes ago (folkway 10); an entering is the room's attention, and tends it
# (a weather over the room rains while someone has been there that day). A choice offers itself only while its guard
# holds (the inside over the variables, the outside by the host with the reader as `request.subject`);
# taking it applies its effects to the variables in order, writes `chose` (the rules its casts are
# fire on it, under the room's owner) and moves the reader to its target, or out at END. `say`, `emote` and `whisper` reach those in the room (folkway 5); a
# thing lying here is taken and put down by its own call, while its taker is here. A ```spween
# block posted here makes a room of its own under this one, by the room's kind.
type State = Super.State
type Choice = Scenes.Choice
type Passage = Scenes.Passage
type Var = Scenes.Var
type Presence = Rows.Presence
type Mine = Rows.Mine
type Departure = Rows.Departure
type Done = Scenes.Done
def leftKey(l: Departure) -> {at: Nat, who: String}:
  {at: l.at, who: l.who}
def holds(relation: Relations.Relation<Plans.Reference>, reference: Plans.Reference) -> Bool:
  Relations.has(relation, {object: reference.object})
def here(context: Abi.Context, state: State) -> Lists.Maybe<Presence>:
  Rows.presentAs(state.presence, context.principal)
def isHere(context: Abi.Context, state: State) -> Bool:
  Rows.isPresent(state.presence, context.principal)
# A call reads the room with the calling turn's staged entry or departure applied.
def present(state: State, context: Abi.Context) -> Activity<Bool>:
  isHere(context, state)
# Principals with avatars index presence in the room's transaction; a principal without an
# avatar still enters and leaves. A failed index refuses the presence write with it.
sum Indexed:
  indexed: {}
def indexPresence(context: Abi.Context) -> Activity<Indexed>:
  match world.viewField::<Plans.Reference>({object: {world: context.world, object: context.principal}, field: "at"}):
    case viewed(_): indexAvatar(context)
    case denied(_): Indexed.indexed({})
    case refused(_): Indexed.indexed({})
def indexAvatar(context: Abi.Context) -> Activity<Indexed>:
  match world.call::<Bool>({object: {world: context.world, object: context.principal}, method: "locate", argument: Plans.nothing()}):
    case returned(r): if r.result then Indexed.indexed({}) else refuse("refused location: The avatar did not index presence.")
    case refused(r): refuse("refused {r.clause}: {r.reading}")
    case denied(_): refuse("refused location: The avatar did not admit its room.")
# The reader's own variables here (`my.<name>`, Record's `mine`), and what a guard reads for them: the room's and theirs.
def ownOf(who: String, state: State) -> Lists.List<Var>:
  Lists.map(Lists.filter(Relations.rows(state.mine), fn(m: Mine) -> Bool: m.who == who), fn(m: Mine) -> Var: {name: m.name, value: m.value})
def readerVars(who: String, state: State) -> Lists.List<Var>:
  Scenes.readerVars(state.vars, ownOf(who, state))
# This turn's draw (Scenes.turnDraw: its entry's height, principal and intent), and the one a reader's passage reads
# its text with: the turn's that brought them there, kept on their presence; for one not here, this turn's.
def turnDraw(context: Abi.Context) -> Nat:
  Scenes.turnDraw(context.height, context.principal, context.intent)
def readerDraw(context: Abi.Context, state: State) -> Nat:
  match here(context, state):
    case some(p): p.value.draw
    case none(_): turnDraw(context)
def passageAt(id: String, state: State) -> Lists.Maybe<Passage>:
  Lists.find(state.passages, fn(p: Passage) -> Bool: p.id == id)
# A passage's guard belongs to the destination, including one stitched after its incoming choices.
def passageGuard(id: String, state: State) -> Scenes.Guard:
  if id == Scenes.ending() then Lists.List.nil() else guardAt(passageAt(id, state))
def guardAt(found: Lists.Maybe<Passage>) -> Scenes.Guard:
  match found:
    case none(_): Lists.List.nil()
    case some(p): p.value.guard
def entryGuard(state: State) -> Scenes.Guard:
  Lists.concat(state.requires, passageGuard(state.start, state))
# A draw's destination is judged after drawing; its card offers the choice's own guard until then.
def choiceGuard(c: Choice, state: State) -> Scenes.Guard:
  match c.among:
    case nil(_): Lists.concat(c.guard, passageGuard(c.to, state))
    case cons(_): c.guard
def lastLeft(who: String, state: State) -> Lists.Maybe<Departure>:
  Lists.find(Relations.rows(state.left), fn(l: Departure) -> Bool: l.who == who)
def cooling(who: String, clock: Nat, state: State) -> Bool:
  match lastLeft(who, state):
    case some(l): clock < l.value.at + state.cooldown
    case none(_): false
def leftAt(who: String, state: State) -> Nat:
  match lastLeft(who, state):
    case some(l): l.value.at
    case none(_): 0n
# What a spell may do, judged purely before anything is written: a refusal names itself and
# writes nothing. A move carries the edits of its effects, one write each, in order, and the room's variables and the
# reader's own as they leave them.
sum Verdict:
  enter: {at: String}
  move: {me: Presence, choice: Choice, chose: String, vars: Lists.List<Edit>, after: Lists.List<Var>, own: Lists.List<Var>}
  leave: {me: Presence, chose: String, vars: Lists.List<Edit>, after: Lists.List<Var>, own: Lists.List<Var>}
  no: Card.Refusal
def no(clause: String, reading: String) -> Verdict:
  Verdict.no({clause: clause, reading: reading})
def enterVerdict(context: Abi.Context, state: State) -> Verdict:
  if lapsed(state, context) then met(context, state) else enterNow(context, state)
def enterNow(context: Abi.Context, state: State) -> Verdict:
  match here(context, state):
    case some(_): no("alreadyHere", "Already here.")
    case none(_): if Relations.count(state.presence) >= Super.presenceMax() then no("roomFull", "{natText(Super.presenceMax())} are already here.") else met(context, state)
# Presence lapses with the room's quiet (obend: ultimo92 "Here" in room/7 from 04:40 to 12:58, #1335 to #2122). A
# presence row keeps no time of its own, and the room's `tended` is the clock of its last attention: an entry, a
# choice, a word said aloud. A room untended for `quietMinutes` shows whoever it held as last here, not here; the
# next entry takes their rows off in its own journaled write, so the record says who left with the entry that found
# the room empty, and a replay derives the same. Never by a timer: nothing is written while nobody comes.
def quietMinutes() -> Nat:
  360n
def lapsed(state: State, context: Abi.Context) -> Bool:
  Relations.count(state.presence) > 0n && state.tended + quietMinutes() <= context.clock
def lapsing(rows: Lists.List<Presence>) -> Activity<Indexed>:
  match rows:
    case nil(_): Indexed.indexed({})
    case cons(c): lapsingOne(c.head.who, c.tail)
def lapsingOne(who: String, rest: Lists.List<Presence>) -> Activity<Indexed>:
  let written(_) = write {presence: retract {who: who}}
  lapsing(rest)
def met(context: Abi.Context, state: State) -> Verdict:
  match Scenes.failing(entryGuard(state), readerVars(context.principal, state)):
    case some(c): no("unmet", "The room requires {Scenes.clauseText(c.value)}.")
    case none(_): if cooling(context.principal, context.clock, state) then no("cooldown", "One who left enters again only after the cooldown: from clock {natText(leftAt(context.principal, state) + state.cooldown)}.") else roomy(context, state)
# `left` keeps Super.leftMax() departures, and one still cooling must never be dropped for a newer
# one: everyone here may leave, so an entry is refused while the cooling departures, the readers
# here and this one would pass the limit.
def roomy(context: Abi.Context, state: State) -> Verdict:
  let cooled = Lists.filter(Relations.rows(state.left), fn(l: Departure) -> Bool: context.clock < l.at + state.cooldown)
  if state.cooldown > 0n && Lists.length(cooled) + Relations.count(state.presence) + 1n > Super.leftMax() then crowded(cooled, state) else Verdict.enter({at: state.start})
def crowded(cooled: Lists.List<Departure>, state: State) -> Verdict:
  match cooled:
    case cons(c): no("crowded", "{natText(Lists.length(cooled))} left within the cooldown; enter again from clock {natText(c.head.at + state.cooldown)}.")
    case nil(_): no("crowded", "The room is full; enter when one leaves.")
def leaveVerdict(context: Abi.Context, state: State) -> Verdict:
  match here(context, state):
    case none(_): no("notHere", "Not here.")
    case some(p): Verdict.leave({me: p.value, chose: "", vars: Lists.List.nil(), after: state.vars, own: ownOf(p.value.who, state)})
def chooseVerdict(choice: String, context: Abi.Context, state: State) -> Verdict:
  match here(context, state):
    case none(_): no("notHere", "Enter the room first.")
    case some(p): standing(p.value, choice, state, turnDraw(context))
def standing(me: Presence, choice: String, state: State, draw: Nat) -> Verdict:
  match passageAt(me.at, state):
    case none(_): no("noPassage", "This room has no passage to choose from.")
    case some(ps): offered(me, Lists.find(ps.value.choices, fn(c: Choice) -> Bool: c.label == choice), Scenes.chosen(me.at, placeOf(ps.value.choices, choice, 1n)), choice, state, draw)
# The place of the choice so labelled among a passage's, from 1.
def placeOf(choices: Lists.List<Choice>, label: String, n: Nat) -> Nat:
  match choices:
    case nil(_): n
    case cons(c): if c.head.label == label then n else placeOf(c.tail, label, n + 1n)
def offered(me: Presence, found: Lists.Maybe<Choice>, chose: String, choice: String, state: State, draw: Nat) -> Verdict:
  match found:
    case none(_): no("notOffered", "{me.at} does not offer {choice}")
    case some(c): guarded(me, drawnTo(c.value, chose, draw), chose, state)
def guarded(me: Presence, c: Choice, chose: String, state: State) -> Verdict:
  match Scenes.failing(choiceGuard(c, state), readerVars(me.who, state)):
    case some(f): no("guarded", "{c.label} needs {Scenes.clauseText(f.value)}.")
    case none(_): arriving(me, c, chose, state)
# A choice that draws between targets goes to the one this turn's draw lands on for it (Scenes.drawFor its `chose`).
def drawnTo(c: Choice, chose: String, draw: Nat) -> Choice:
  match c.among:
    case nil(_): c
    case cons(_): extend(c, {to: Scenes.drawn(c.among, Scenes.drawFor(draw, chose)), among: Lists.List.nil()})
def arriving(me: Presence, c: Choice, chose: String, state: State) -> Verdict:
  if c.to == Scenes.ending() then effected(me, c, chose, effectsOf(c, me.who, state), true) else reached(me, c, chose, state)
def reached(me: Presence, c: Choice, chose: String, state: State) -> Verdict:
  match passageAt(c.to, state):
    case none(_): no("noPassage", "{c.to} is not in this room")
    case some(_): effected(me, c, chose, effectsOf(c, me.who, state), false)
def effected(me: Presence, c: Choice, chose: String, edits: Effects, ends: Bool) -> Verdict:
  match edits:
    case full(f): no("varsFull", f.reading)
    case edits(e): if ends then Verdict.leave({me: me, chose: chose, vars: e.edits, after: e.vars, own: e.own}) else Verdict.move({me: me, choice: c, chose: chose, vars: e.edits, after: e.vars, own: e.own})
# The outside of a guard, judged in the turn: each condition in the law's grammar by the host, of the
# room as it stands with the reader as `request.subject` (`world.holds`), and what the reader keeps or
# holds by the thing's own `keeper` and `holder`, in order; the first that fails, if any. A condition
# the host cannot read fails, with its reason.
sum Judged:
  held: {}
  failed: {clause: Scenes.Clause, why: String}
def judging(guard: Scenes.Guard, context: Abi.Context) -> Activity<Judged>:
  match guard:
    case nil(_): Judged.held({})
    case cons(c): judgedOne(c.head, c.tail, context)
def judgedOne(clause: Scenes.Clause, rest: Scenes.Guard, context: Abi.Context) -> Activity<Judged>:
  match clause.test:
    case law(_): lawJudged(clause, rest, context)
    case holding(_): thingJudged(clause, rest, true, context)
    case lacking(_): thingJudged(clause, rest, false, context)
    case _: judging(rest, context)
def lawJudged(clause: Scenes.Clause, rest: Scenes.Guard, context: Abi.Context) -> Activity<Judged>:
  match world.holds({object: Plans.self(context), when: clause.value}):
    case held(h): if h.value then judging(rest, context) else Judged.failed({clause: clause, why: ""})
    case refused(r): Judged.failed({clause: clause, why: r.reading})
def thingJudged(clause: Scenes.Clause, rest: Scenes.Guard, wanted: Bool, context: Abi.Context) -> Activity<Judged>:
  match hasThing(clause.key, clause.value, context):
    case yes(_): if wanted then judging(rest, context) else Judged.failed({clause: clause, why: ""})
    case no(_): if wanted then Judged.failed({clause: clause, why: ""}) else judging(rest, context)
# Whether the reader keeps a thing (its `keeper`) or holds it (its `holder`), as the thing says.
sum Had:
  yes: {}
  no: {}
def had(b: Bool) -> Had:
  if b then Had.yes({}) else Had.no({})
def hasThing(category: String, thing: String, context: Abi.Context) -> Activity<Had>:
  if category == "holds" then holdsThing(thing, context) else keepsThen(category == "has", thing, context)
def keepsThen(orHolds: Bool, thing: String, context: Abi.Context) -> Activity<Had>:
  match world.viewField::<String>({object: {world: context.world, object: thing}, field: "keeper"}):
    case viewed(v): if v.state == context.principal then Had.yes({}) else if orHolds then holdsThing(thing, context) else Had.no({})
    case _: if orHolds then holdsThing(thing, context) else Had.no({})
def holdsThing(thing: String, context: Abi.Context) -> Activity<Had>:
  match world.viewField::<Plans.Reference>({object: {world: context.world, object: thing}, field: "holder"}):
    case viewed(v): had(v.state.object == context.principal)
    case _: Had.no({})
def failedReading(c: Scenes.Clause, why: String) -> String:
  if why == "" then Scenes.clauseText(c) else "{Scenes.clauseText(c)} ({why})"
# A verdict whose choice (or, entering, the scene's requirements) has an outside, judged before anything
# is written: a clause that fails refuses it by name.
def outward(verdict: Verdict, context: Abi.Context, state: State) -> Activity<Verdict>:
  match verdict:
    case enter(_): outwardBy(verdict, Scenes.outside(entryGuard(state)), "unmet", "The room requires ", context)
    case move(m): outwardBy(verdict, Scenes.outside(choiceGuard(m.choice, state)), "guarded", "{m.choice.label} needs ", context)
    case leave(l): if l.chose == "" then verdict else outwardBy(verdict, Scenes.outside(choiceAt(l.me.at, l.chose, state).guard), "guarded", "{choiceAt(l.me.at, l.chose, state).label} needs ", context)
    case no(_): verdict
def outwardBy(verdict: Verdict, guard: Scenes.Guard, clause: String, lead: String, context: Abi.Context) -> Activity<Verdict>:
  match judging(guard, context):
    case held(_): verdict
    case failed(f): no(clause, "{lead}{failedReading(f.clause, f.why)}.")
# The choice a `chose` names in a passage.
def choiceAt(at: String, chose: String, state: State) -> Choice:
  match passageAt(at, state):
    case some(p): nthChoice(p.value.choices, chose, at, 1n)
    case none(_): noChoice()
def noChoice() -> Choice:
  {label: "", to: "", effects: Lists.List.nil(), guard: Lists.List.nil(), text: "", among: Lists.List.nil()}
def nthChoice(choices: Lists.List<Choice>, chose: String, at: String, n: Nat) -> Choice:
  match choices:
    case nil(_): noChoice()
    case cons(c): if Scenes.chosen(at, n) == chose then c.head else nthChoice(c.tail, chose, at, n + 1n)
# The edits a choice's effects make, in order, each against the variables as the ones before it leave them, and the
# room's variables and the reader's own as they stand after them all. The room keeps sixteen variables of its own,
# sixteen of each reader's, and Super.mineMax() of its readers' together: one more is refused, never dropped.
sum Edit:
  room: {entry: Plans.Entries<Var, Var>}
  own: {row: Mine}
sum Effects:
  edits: {edits: Lists.List<Edit>, vars: Lists.List<Var>, own: Lists.List<Var>}
  full: {reading: String}
def effectsOf(c: Choice, who: String, state: State) -> Effects:
  effects(c.effects, who, state.vars, ownOf(who, state), Relations.count(state.mine), Lists.List.nil())
def effects(list: Lists.List<Scenes.Effect>, who: String, vars: Lists.List<Var>, own: Lists.List<Var>, rows: Nat, edits: Lists.List<Edit>) -> Effects:
  match list:
    case nil(_): Effects.edits({edits: edits, vars: vars, own: own})
    case cons(c): if Scenes.varies(c.head) == false then effects(c.tail, who, vars, own, rows, edits) else if Scenes.isMine(c.head.key) then ownOn(c.head, Scenes.ownName(c.head.key), c.tail, who, vars, own, rows, edits, Lists.find(own, fn(v: Var) -> Bool: v.name == Scenes.ownName(c.head.key))) else effectOn(c.head, c.tail, who, vars, own, rows, edits, Lists.find(vars, fn(v: Var) -> Bool: v.name == c.head.key))
def effectOn(effect: Scenes.Effect, rest: Lists.List<Scenes.Effect>, who: String, vars: Lists.List<Var>, own: Lists.List<Var>, rows: Nat, edits: Lists.List<Edit>, found: Lists.Maybe<Var>) -> Effects:
  match found:
    case some(v): effects(rest, who, Lists.map(vars, fn(w: Var) -> Var: if w.name == effect.key then {name: w.name, value: Scenes.applied(effect, w.value)} else w), own, rows, Lists.append(edits, Edit.room({entry: Plans.Entries.amendItem({item: v.value, change: {name: effect.key, value: Scenes.applied(effect, v.value.value)}})})))
    case none(_): if Lists.length(vars) >= 16n then Effects.full({reading: "Sixteen variables are already set."}) else effects(rest, who, Lists.append(vars, {name: effect.key, value: Scenes.applied(effect, "")}), own, rows, Lists.append(edits, Edit.room({entry: Plans.Entries.append({item: {name: effect.key, value: Scenes.applied(effect, "")}})})))
def ownOn(effect: Scenes.Effect, name: String, rest: Lists.List<Scenes.Effect>, who: String, vars: Lists.List<Var>, own: Lists.List<Var>, rows: Nat, edits: Lists.List<Edit>, found: Lists.Maybe<Var>) -> Effects:
  match found:
    case some(v): effects(rest, who, vars, Lists.map(own, fn(w: Var) -> Var: if w.name == name then {name: name, value: Scenes.applied(effect, w.value)} else w), rows, Lists.append(edits, Edit.own({row: {who: who, name: name, value: Scenes.applied(effect, v.value.value)}})))
    case none(_): if Lists.length(own) >= 16n then Effects.full({reading: "Sixteen of your own variables are already set here."}) else if rows >= Super.mineMax() then Effects.full({reading: "This room keeps {natText(Super.mineMax())} of its readers' own variables, and holds that many."}) else effects(rest, who, vars, Lists.append(own, {name: name, value: Scenes.applied(effect, "")}), rows + 1n, Lists.append(edits, Edit.own({row: {who: who, name: name, value: Scenes.applied(effect, "")}})))
# The reader's own variables, as a choice left them, in the room's `mine`.
def ownAfter(mine: Relations.Relation<Mine>, who: String, own: Lists.List<Var>) -> Relations.Relation<Mine>:
  match own:
    case nil(_): mine
    case cons(c): ownAfter(Relations.upsert(mine, {who: who, name: c.head.name, value: c.head.value}, Rows.mineKey), who, c.tail)
# Performs a verdict: a refusal writes nothing and is answered by its clause (Card.refusing);
# otherwise the writes, in order, and the reader is shown the room as they leave it.
def done(verdict: Verdict, context: Abi.Context, state: State) -> Activity<Done>:
  match outward(verdict, context, state):
    case no(n): roomRefused(n, context)
    case enter(e): performed(Verdict.enter(e), context, state)
    case move(m): performed(Verdict.move(m), context, state)
    case leave(l): performed(Verdict.leave(l), context, state)
def performed(verdict: Verdict, context: Abi.Context, state: State) -> Activity<Done>:
  match verdict:
    case no(n): roomRefused(n, context)
    case enter(e): if lapsed(state, context) then quietEntry(e.at, state, context) else entering(e.at, extend(state, {presence: Relations.insert(state.presence, {who: context.principal, handle: context.handle, at: e.at, draw: turnDraw(context)}, Rows.presenceKey), tended: context.clock}), context)
    case move(m): moving(m.me, m.choice, m.chose, m.vars, extend(state, {presence: Relations.upsert(state.presence, {who: m.me.who, handle: m.me.handle, at: m.choice.to, draw: turnDraw(context)}, Rows.presenceKey), vars: m.after, mine: ownAfter(state.mine, m.me.who, m.own), chose: m.chose, tended: context.clock}), context)
    case leave(l): departing(l.me, l.chose, l.vars, extend(state, {presence: Relations.retract(state.presence, {who: l.me.who}, Rows.presenceKey), vars: l.after, mine: ownAfter(state.mine, l.me.who, l.own), chose: if l.chose == "" then state.chose else l.chose}), context, state)
def quietEntry(at: String, state: State, context: Abi.Context) -> Activity<Done>:
  match lapsing(Relations.rows(state.presence)):
    case indexed(_): entering(at, extend(state, {presence: Relations.insert(Relations.empty(), {who: context.principal, handle: context.handle, at: at, draw: turnDraw(context)}, Rows.presenceKey), tended: context.clock}), context)
def entering(at: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {presence: insert {who: context.principal, handle: context.handle, at: at, draw: turnDraw(context)}, tended: set context.clock}
  match indexPresence(context):
    case indexed(_): entryTold(at, after, context)
def entryTold(at: String, after: State, context: Abi.Context) -> Activity<Done>:
  match lastLeft(context.principal, after):
    case some(l): if l.value.entry == 0n then welcomed(at, after, Document.text(""), context) else away(at, after, l.value.entry, context)
    case none(_): welcomed(at, after, Document.text(""), context)
def welcomed(at: String, after: State, lead: Document.Document, context: Abi.Context) -> Activity<Done>:
  match seen(after, context):
    case seen(v): Card.tell(Done.entered({at: at}), Document.concat(lead, Super.render(v.state, context)))
# One who comes back is told first what happened here while they were away, since the entry they
# left at: the lines others wrote here, newest first, then what changed under the room (`world.changed`
# under its id: rooms carved in it, things made here), three lines at most.
def away(at: String, after: State, since: Nat, context: Abi.Context) -> Activity<Done>:
  match world.changed({since: since, prefix: "{context.object}/", after: ""}):
    case changed(c): welcomed(at, after, awayLines(after, since, c.items, context), context)
    case denied(_): welcomed(at, after, awayLines(after, since, Lists.List.nil(), context), context)
def awayLines(state: State, since: Nat, changes: Lists.List<World.Change>, context: Abi.Context) -> Document.Document:
  let written = Lists.map(Lists.filter(Lists.reverse(Relations.order(state.lines, fn(l: Rows.Line) -> Nat: l.n)), fn(l: Rows.Line) -> Bool: l.at > since && l.author != context.principal), fn(l: Rows.Line) -> String: "{Card.shown(l.author, l.handle, context)}: {clip(l.text)}\n")
  let made = Lists.map(changes, fn(c: World.Change) -> String: "{c.object.object} v{natText(c.version)} by {Card.handle(c.by)}\n")
  let rows = Lists.take(Lists.concat(written, made), 3n)
  if Lists.length(rows) == 0n then Document.text("") else Document.text("While you were away (since entry {natText(since)}):\n{textJoin(rows, \"\")}\n")
def clip(text: String) -> String:
  if textLength(text) > 100n then "{textTake(text, 99n)}…" else text
def moving(me: Presence, c: Choice, chose: String, vars: Lists.List<Edit>, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {presence: upsert {who: me.who, handle: me.handle, at: c.to, draw: turnDraw(context)}, chose: set chose, tended: set context.clock}
  varied(vars, Done.moved({to: c.to}), c.text, after, context)
# The variables' edits in order, then the reader is shown what taking the choice said (its `text`, "" for none) and the
# room as the turn leaves it.
def varied(vars: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  match vars:
    case nil(_): told(result, said, after, context)
    case cons(c): wroteVar(c.head, c.tail, result, said, after, context)
# What a choice said comes first: above the passage it led to, so the card's budget counts it with the passage, or
# alone above the room when it ended the visit.
def told(result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let shown = Spween.rendered(said, readerVars(context.principal, after), readerDraw(context, after))
  match seen(after, context):
    case seen(v): Card.tell(result, if shown == "" then Super.render(v.state, context) else if isHere(context, after) then Super.render(saying(said, v.state, context), context) else Document.concat(Document.text("{shown}\n\n"), Super.render(v.state, context)))
def saying(said: String, state: State, context: Abi.Context) -> State:
  match here(context, state):
    case some(p): extend(state, {passages: Lists.map(state.passages, fn(q: Passage) -> Passage: if q.id == p.value.at then extend(q, {text: "{said}\n\n{q.text}"}) else q)})
    case none(_): state
# The room as the turn's reader sees it: at their passage, the choices whose outside holds, shown without
# it (a pure read of the card judges the inside alone and shows the outside beside each choice).
sum Seen:
  seen: {state: State}
def seen(state: State, context: Abi.Context) -> Activity<Seen>:
  match here(context, state):
    case none(_): Seen.seen({state: state})
    case some(p): seenAt(passageAt(p.value.at, state), state, context)
def seenAt(found: Lists.Maybe<Passage>, state: State, context: Abi.Context) -> Activity<Seen>:
  match found:
    case none(_): Seen.seen({state: state})
    case some(ps): seenSifted(ps.value, state, context)
def seenSifted(ps: Passage, state: State, context: Abi.Context) -> Activity<Seen>:
  match sifted(ps.choices, state, context):
    case sifted(k): Seen.seen({state: extend(state, {passages: Lists.map(state.passages, fn(p: Passage) -> Passage: extend(p, {guard: Lists.filter(p.guard, Scenes.inside), choices: if p.id == ps.id then k.choices else p.choices}))})})
sum Sifted:
  sifted: {choices: Lists.List<Choice>}
def sifted(choices: Lists.List<Choice>, state: State, context: Abi.Context) -> Activity<Sifted>:
  match choices:
    case nil(_): Sifted.sifted({choices: Lists.List.nil()})
    case cons(c): siftedOne(c.head, c.tail, state, context)
def siftedOne(c: Choice, rest: Lists.List<Choice>, state: State, context: Abi.Context) -> Activity<Sifted>:
  match judging(Scenes.outside(choiceGuard(c, state)), context):
    case held(_): keptBefore(extend(c, {guard: Lists.filter(c.guard, Scenes.inside)}), rest, state, context)
    case failed(_): sifted(rest, state, context)
def keptBefore(c: Choice, rest: Lists.List<Choice>, state: State, context: Abi.Context) -> Activity<Sifted>:
  match sifted(rest, state, context):
    case sifted(r): Sifted.sifted({choices: Lists.List.cons({head: c, tail: r.choices})})
def wroteVar(edit: Edit, rest: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  match edit:
    case room(r): wroteRoom(r.entry, rest, result, said, after, context)
    case own(o): wroteOwn(o.row, rest, result, said, after, context)
def wroteRoom(entry: Plans.Entries<Var, Var>, rest: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {vars: entry}))
  varied(rest, result, said, after, context)
def wroteOwn(row: Mine, rest: Lists.List<Edit>, result: Done, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {mine: upsert row}
  varied(rest, result, said, after, context)
# Leaving (or reaching END) records when, for the cooldown: the reader's last departure is
# retracted and this one inserted.
def departing(me: Presence, chose: String, vars: Lists.List<Edit>, after: State, context: Abi.Context, state: State) -> Activity<Done>:
  let written(_) = write {presence: retract {who: me.who}, chose: set if chose == "" then state.chose else chose}
  match indexPresence(context):
    case indexed(_): departureRecorded(me, chose, vars, after, context, state)
def departureRecorded(me: Presence, chose: String, vars: Lists.List<Edit>, after: State, context: Abi.Context, state: State) -> Activity<Done>:
  match lastLeft(me.who, state):
    case some(l): forgotten(l.value, vars, choiceAt(me.at, chose, state).text, after, context)
    case none(_): recorded(vars, choiceAt(me.at, chose, state).text, after, context)
def forgotten(last: Departure, vars: Lists.List<Edit>, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {left: retract leftKey(last)}
  recorded(vars, said, after, context)
def recorded(vars: Lists.List<Edit>, said: String, after: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {left: insert {at: context.clock, who: context.principal, entry: context.height}}
  varied(vars, Done.left({}), said, after, context)
form enter:
form choose:
  choice: text 1..64
form leave:
form say:
  line: text 1..280
form emote:
  line: text 1..280
form whisper:
  to: text 1..160
  line: text 1..280
form write:
  text: text 1..280
form chime:
form lines:
  from: natural 0..1000000000
form door:
  label: text 1..32
  to: text 1..128
form undoor:
  label: text 1..32
# The scene, replaced or extended by its owner: `rescene` replaces it whole in one turn, read as making reads it
# (Spween.parse: every target answered); the station stitches a wing a turn (Spween.wing) and restitches from its first.
# Each outside guard is read once by the host, so one it cannot read refuses by name; the casts are rules of the room's
# text, the old scene's removed and the new one's added (`stagedLawWithout`, the owner's other clauses kept), then the
# passages and requirements are written, and after the last wing a reader at a passage no longer there stands at the start.
# Variables, lines, presence and what lies here stay. `mark`, when not "", is kept as the wing's mark.
record Staging:
  text: String
  replace: Bool
  finish: Bool
  mark: String
form rescene:
  spween: text 1..16384
def rescene(state: State, input: ResceneInput, context: Abi.Context) -> Activity<Done>:
  if context.principal != state.owner then roomRefused({clause: "notOwner", reading: "Only the room's owner replaces its scene; a ```spween block posted here makes a room of your own under it."}, context) else staging(Spween.parse(input.spween), {text: input.spween, replace: true, finish: true, mark: ""}, state, context)
def staging(parsed: Spween.Parsed, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  match parsed:
    case refused(r): roomRefused({clause: "spween", reading: r.reason}, context)
    case parsed(p): if Spween.joined(if work.replace then Lists.List.nil() else state.passages, p.seed.passages) != "" then roomRefused({clause: "spween", reading: Spween.joined(if work.replace then Lists.List.nil() else state.passages, p.seed.passages)}, context) else stageGuards(p.seed, Scenes.conditions(p.seed), work, state, context)
def stageGuards(wing: Scenes.Seed, conditions: Lists.List<String>, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  match conditions:
    case nil(_): stageRules(wing, work, state, context)
    case cons(c): stageGuard(wing, c.head, c.tail, work, state, context)
def stageGuard(wing: Scenes.Seed, condition: String, rest: Lists.List<String>, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  match world.holds({object: Plans.self(context), when: condition}):
    case held(_): stageGuards(wing, rest, work, state, context)
    case refused(r): roomRefused({clause: "spween", reading: "The guard `{condition}` does not read: {r.reading}"}, context)
def stageRules(wing: Scenes.Seed, work: Staging, state: State, context: Abi.Context) -> Activity<Done>:
  let all = Lists.concat(if work.replace then Lists.List.nil() else state.passages, wing.passages)
  match world.inspect({object: Plans.self(context), source: false}):
    case inspected(i): stageAmend(wing, work, all, "{if work.replace then stagedLawWithout(i.law, Scenes.castsOf(state.passages)) else i.law}{Scenes.ruleText(wing.passages)}", state, context)
    case _: roomRefused({clause: "spween", reading: "The room's text is not readable here; its scene stands as it was."}, context)
# Scene rules have the names of their passage, choice and cast. Only those rule lines are removed;
# the owner's other laws, rules and wants retain their text, wherever they stand in the law.
def stagedLawWithout(law: String, casts: Lists.List<Scenes.Cast>) -> String:
  let at = Text.indexOf(law, "\n")
  let line = textTake(law, at)
  let kept = if Lists.any(casts, fn(c: Scenes.Cast) -> Bool: Text.starts(line, "rule {c.id}_{natText(c.n)}{Scenes.suffix(c.k)} ") || Text.starts(line, "rule {c.id}_{natText(c.n)}{Scenes.suffix(c.k)}:")) then "" else line
  if at == textLength(law) then kept else stagedJoined(kept, stagedLawWithout(textDrop(law, at + 1n), casts))
def stagedJoined(first: String, rest: String) -> String:
  if first == "" then rest else if rest == "" then first else "{first}\n{rest}"
def stageAmend(wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, law: String, state: State, context: Abi.Context) -> Activity<Done>:
  match world.amend({object: Plans.self(context), law: law}):
    case amended(_): stageCleared(if work.replace then state.passages else Lists.List.nil(), if work.replace then state.requires else Lists.List.nil(), wing, work, all, state, context)
    case refused(r): roomRefused({clause: r.clause, reading: "The scene's casts are rules the room's text could not take: {r.reading}"}, context)
def stageCleared(passages: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  match passages:
    case nil(_): stageRequirementsCleared(requires, wing, work, all, state, context)
    case cons(c): stagePassageCleared(c.head, c.tail, requires, wing, work, all, state, context)
def stagePassageCleared(passage: Scenes.Passage, rest: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {passages: Plans.Entries.removeItem({item: passage})}))
  stageCleared(rest, requires, wing, work, all, state, context)
def stageRequirementsCleared(requires: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  match requires:
    case nil(_): stageWrite(wing, work, all, state, context)
    case cons(c): stageRequirementCleared(c.head, c.tail, wing, work, all, state, context)
def stageRequirementCleared(clause: Scenes.Clause, rest: Scenes.Guard, wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {requires: Plans.Entries.removeItem({item: clause})}))
  stageRequirementsCleared(rest, wing, work, all, state, context)
def stageWrite(wing: Scenes.Seed, work: Staging, all: Lists.List<Scenes.Passage>, state: State, context: Abi.Context) -> Activity<Done>:
  let first = work.replace || Lists.length(state.passages) == 0n
  let start = if first then wing.start else state.start
  let written(_) = write {start: set start, cooldown: set if first then wing.cooldown else state.cooldown}
  stageMarked(work, wing, all, if first then wing.requires else Lists.List.nil(), if work.finish then Relations.rows(state.presence) else Lists.List.nil(), start, context)
# A wing's mark, when its maker keeps one (the station's chained digest); a room's rescene keeps none.
def stageMarked(work: Staging, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, requires: Scenes.Guard, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  if work.mark == "" then stagePassages(wing.passages, requires, wing, all, presence, start, context) else stageStamped(work.mark, wing, all, requires, presence, start, context)
def stageStamped(mark: String, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, requires: Scenes.Guard, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {marks: insert {name: mark, who: context.principal, handle: context.handle, at: context.height}}
  stagePassages(wing.passages, requires, wing, all, presence, start, context)
# One write a passage and a clause, each appended, as the room writes its variables.
def stagePassages(passages: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  match passages:
    case nil(_): stageRequires(requires, wing, all, presence, start, context)
    case cons(c): stagePassage(c.head, c.tail, requires, wing, all, presence, start, context)
def stagePassage(passage: Scenes.Passage, rest: Lists.List<Scenes.Passage>, requires: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {passages: Plans.Entries.append({item: passage})}))
  stagePassages(rest, requires, wing, all, presence, start, context)
def stageRequires(requires: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  match requires:
    case nil(_): stagePresence(presence, wing, all, start, context)
    case cons(c): stageRequire(c.head, c.tail, wing, all, presence, start, context)
def stageRequire(clause: Scenes.Clause, rest: Scenes.Guard, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, presence: Lists.List<Rows.Presence>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = world.write(extend(keep(), {requires: Plans.Entries.append({item: clause})}))
  stageRequires(rest, wing, all, presence, start, context)
def stagePresence(presence: Lists.List<Rows.Presence>, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, start: String, context: Abi.Context) -> Activity<Done>:
  match presence:
    case nil(_): Card.tell(Done.done({}), Document.text("Stitched {natText(Lists.length(wing.passages))} passages; the scene holds {natText(Lists.length(all))}. {stageWaiting(Spween.waiting(all))}\n"))
    case cons(c): if Lists.any(all, fn(p: Scenes.Passage) -> Bool: p.id == c.head.at) then stagePresence(c.tail, wing, all, start, context) else stageMoved(c.head, c.tail, wing, all, start, context)
def stageMoved(me: Rows.Presence, rest: Lists.List<Rows.Presence>, wing: Scenes.Seed, all: Lists.List<Scenes.Passage>, start: String, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {presence: upsert {who: me.who, handle: me.handle, at: start, draw: me.draw}}
  stagePresence(rest, wing, all, start, context)
def stageWaiting(waiting: Lists.List<String>) -> String:
  match waiting:
    case nil(_): "Every choice leads to a passage."
    case cons(_): "Still waiting for: {textJoin(waiting, \", \")}."
def enter(state: State, context: Abi.Context) -> Activity<Done>:
  done(enterVerdict(context, state), context, state)
def choose(state: State, input: ChooseInput, context: Abi.Context) -> Activity<Done>:
  done(chooseVerdict(input.choice, context, state), context, state)
def leave(state: State, context: Abi.Context) -> Activity<Done>:
  done(leaveVerdict(context, state), context, state)
# Talk reaches those in the room (in key order, at most Super.presenceMax()) under the speaker's
# handle; only someone here talks. What is said or done aloud is the room's line too, under the verb
# that said it, so one who comes later is told who spoke here and what was said; a whisper is not.
def say(state: State, input: SayInput, context: Abi.Context) -> Activity<Done>:
  if isHere(context, state) then aloud("say", input.line, "{speaker(context)}: {input.line}\n", state, context) else hushed("say", context)
def emote(state: State, input: EmoteInput, context: Abi.Context) -> Activity<Done>:
  if isHere(context, state) then aloud("emote", input.line, "* {speaker(context)} {input.line}\n", state, context) else hushed("emote", context)
def aloud(verb: String, text: String, line: String, state: State, context: Abi.Context) -> Activity<Done>:
  if Text.blank(text) then roomRefused({clause: "empty", reading: "A line needs words; this {verb} has none, so nothing was said"}, context) else spokenAloud(verb, text, line, state, context)
def spokenAloud(verb: String, text: String, line: String, state: State, context: Abi.Context) -> Activity<Done>:
  let written(_) = write {lines: insert {author: context.principal, handle: context.handle, verb: verb, text: text, at: context.height, n: state.next}, next: add 1n, tended: set context.clock}
  spoken(Relations.rows(state.presence), line)
def whisper(state: State, input: WhisperInput, context: Abi.Context) -> Activity<Done>:
  if isHere(context, state) == false then hushed("whisper", context) else whispered(Lists.find(Relations.rows(state.presence), fn(p: Presence) -> Bool: p.who == input.to || p.handle == input.to), input.to, "{speaker(context)} whispers: {input.line}\n", context)
def whispered(found: Lists.Maybe<Presence>, to: String, line: String, context: Abi.Context) -> Activity<Done>:
  match found:
    case some(p): confirmed(p.value, line, context)
    case none(_): roomRefused({clause: "notHere", reading: "{to} is not here"}, context)
def confirmed(to: Presence, line: String, context: Abi.Context) -> Activity<Done>:
  match spoken(Lists.List.cons({head: to, tail: Lists.List.nil()}), line):
    case _: Card.tell(Done.done({}), Document.text("Whispered to {Card.shown(to.who, to.handle, context)}; no one else here hears it. Say to all: delvetalk {Card.called(context, \"room\")} say / line: <text>.\n"))
def hushed(doing: String, context: Abi.Context) -> Activity<Done>:
  roomRefused({clause: "notPresent", reading: "Only someone here can {doing}; enter first: delvetalk {Card.called(context, \"room\")} enter"}, context)
def roomRefused(refusal: Card.Refusal, context: Abi.Context) -> Activity<Done>:
  Card.refusing(Done.refused(refusal), refusal, context)
def speaker(context: Abi.Context) -> String:
  Card.name(context.principal, context)
def spoken(present: Lists.List<Presence>, line: String) -> Activity<Done>:
  match present:
    case nil(_): Done.done({})
    case cons(c): spokenTo(c.head, c.tail, line)
def spokenTo(to: Presence, rest: Lists.List<Presence>, line: String) -> Activity<Done>:
  match world.offer({to: to.who, document: Document.text(line)}):
    case _: spoken(rest, line)
def write(state: State, input: WriteInput, context: Abi.Context) -> Activity<Nat>:
  Super.write(state, {text: input.text}, context)
# Its bells rung together (RECORD's `ringTogether`): what lies here and what its doors open onto.
def chime(state: State, context: Abi.Context) -> Activity<Nat>:
  Super.ringTogether(state, context)
def lines(state: State, input: LinesInput, context: Abi.Context) -> Activity<Card.Replied>:
  Super.lines(state, {from: input.from}, context)
def door(state: State, input: DoorInput, context: Abi.Context) -> Activity<Card.Replied>:
  Super.door(state, {label: input.label, to: input.to}, context)
def undoor(state: State, input: UndoorInput, context: Abi.Context) -> Activity<Card.Replied>:
  Super.undoor(state, {label: input.label}, context)
# A thing takes itself off the floor into the hands of the principal, who must be here, and puts
# itself down where its holder stands, or where the room's owner lays it; only the thing itself
# (the calling object) does either. A thing lying here that is inspirited gives the room its line,
# under the one who inspirited it, and a mark, so the card lists it among the claimed.
def inspirited(state: State, input: {line: String}, context: Abi.Context) -> Activity<Done>:
  if context.caller == "" then roomRefused({clause: "notAThing", reading: "Only a thing is inspirited."}, context) else if holds(state.things, {world: context.world, object: context.caller}) == false then roomRefused({clause: "notLying", reading: "That is not lying here."}, context) else spirited(input.line, context, state)
def spirited(line: String, context: Abi.Context, state: State) -> Activity<Done>:
  let written(_) = write {lines: insert {author: context.principal, handle: context.handle, verb: "inspirit", text: if textLength(line) > 280n then "{textTake(line, 279n)}…" else line, at: context.height, n: state.next}, next: add 1n, marks: insert {name: claimMark(context.caller), who: context.principal, handle: context.handle, at: context.height}}
  Done.done({})
def claimMark(thing: String) -> String:
  "inspirited {thing}"
def claimed(thing: Plans.Reference, state: State) -> Bool:
  Super.marked(state, claimMark(thing.object))
def take(state: State, context: Abi.Context) -> Activity<Done>:
  if context.caller == "" then roomRefused({clause: "notAThing", reading: "Only a thing can take itself."}, context) else if isHere(context, state) == false then roomRefused({clause: "notPresent", reading: "Only someone here can take things."}, context) else if holds(state.things, {world: context.world, object: context.caller}) == false then roomRefused({clause: "notLying", reading: "That is not lying here."}, context) else lifted(context)
def lifted(context: Abi.Context) -> Activity<Done>:
  let written(_) = write {things: retract {object: context.caller}}
  Done.done({})
def put(state: State, context: Abi.Context) -> Activity<Done>:
  if context.caller == "" then roomRefused({clause: "notAThing", reading: "Only a thing can put itself down."}, context) else if isHere(context, state) == false && context.principal != state.owner then roomRefused({clause: "notPresent", reading: "Only someone here can put things down."}, context) else if holds(state.things, {world: context.world, object: context.caller}) then roomRefused({clause: "alreadyLying", reading: "That is already lying here."}, context) else laid(context)
def laid(context: Abi.Context) -> Activity<Done>:
  let written(_) = write {things: insert {world: context.world, object: context.caller}}
  Done.done({})
def methods() -> Lists.List<String>:
  Text.words("take put inspirited ~present")
def actions(state: State, context: Abi.Context) -> Lists.List<String>:
  Lists.concat(Text.words("enter choose leave say emote whisper write chime lines"), if context.principal == state.owner then Text.words("door undoor rescene") else Text.words(""))
# A ```spween block makes a room under this one, by this room's kind (its `makeUnder`, which reads
# the block); posted by this room's owner, it makes nothing and is answered with the spell that makes it this room's
# scene (`rescene`, the block under it). Anything else is the card's default.
def receive(state: State, input: Card.Reply, context: Abi.Context) -> Activity<Card.Replied>:
  match Spween.block(input.text):
    case some(b): if context.principal == state.owner then rescening(Spween.parse(b.value), context) else authored(b.value, Spween.parse(b.value), context, state)
    case none(_): answered(input, context, state)
def rescening(parsed: Spween.Parsed, context: Abi.Context) -> Activity<Card.Replied>:
  match parsed:
    case refused(r): Super.refused({clause: "spween", reading: r.reason}, context)
    case parsed(p): Card.tell(Card.Replied.usage({}), Document.text("This room is yours, so the block makes no room under it. It reads as a scene of {natText(Lists.length(p.seed.passages))} passages; to make it this room's scene, post it again with this line above it: delvetalk {Card.called(context, \"room\")} rescene\n"))
def answered(input: Card.Reply, context: Abi.Context, state: State) -> Activity<Card.Replied>:
  match seen(state, context):
    case seen(v): Card.answer(input, context, forms(), Super.render(v.state, context))
def authored(block: String, parsed: Spween.Parsed, context: Abi.Context, state: State) -> Activity<Card.Replied>:
  match parsed:
    case refused(r): Super.refused({clause: "spween", reading: r.reason}, context)
    case parsed(p): carving(if p.seed.title == "" then "a room" else p.seed.title, block, context, state)
def fieldOf(name: String, value: String) -> Rows.Field:
  {name: name, value: value}
def carvedFields(title: String, block: String) -> Lists.List<Rows.Field>:
  Lists.append(Lists.append(Lists.List.nil(), fieldOf("name", textTake(title, 64n))), fieldOf("spween", block))
def kindOf(state: State) -> String:
  if state.kind == "" then "kind/room" else state.kind
# A room makes rooms of its own kind (Record.making), each lying here.
def carving(title: String, block: String, context: Abi.Context, state: State) -> Activity<Card.Replied>:
  Super.making(extend(state, {kind: kindOf(state)}), kindOf(state), carvedFields(title, block), context, fn(object: String) -> Document.Document: carved(object, title), fn(m: Super.Made) -> Card.Replied: carvedAs(m))
def carved(object: String, title: String) -> Document.Document:
  Document.text("ROOM {title} is at {object}. Enter: delvetalk {object} enter\n")
def carvedAs(made: Super.Made) -> Card.Replied:
  match made:
    case made(_): Card.Replied.done({action: "create"})
    case refused(r): Card.Replied.refused(r)
# The room's head, over the record's lines and doors: its name and who is here; to a reader in
# it, their passage and the choices whose guards hold, then the variables; to anyone else, the
# spell that enters. What lies here follows.
def title(state: State) -> String:
  let name = if state.head == "" then "A ROOM" else state.head
  if textLength(name) > 120n then "{textTake(name, 119n)}…" else name
def choiceLine(c: Choice) -> String:
  match Scenes.outside(c.guard):
    case nil(_): "  * {c.label}\n"
    case cons(_): "  * {c.label} (if {textJoin(Lists.map(Scenes.outside(c.guard), Scenes.clauseText), \", \")})\n"
def varLine(v: Var) -> String:
  "{v.name} = {v.value}\n"
# At most eight of a list, then how many more.
def firstEight(lines: Lists.List<String>) -> String:
  let more = if Lists.length(lines) > 8n then "… and {natText(Lists.length(lines) - 8n)} more\n" else ""
  "{textJoin(Lists.take(lines, 8n), \"\")}{more}"
# What lies here, the unclaimed first with the spell that inspirits one, each line at most 400
# characters, eight things in all, then how many more.
def lyingLine(state: State) -> String:
  let all = Lists.take(Relations.rows(state.things), 8n)
  let open = Lists.filter(all, fn(t: Plans.Reference) -> Bool: claimed(t, state) == false)
  let kept = Lists.filter(all, fn(t: Plans.Reference) -> Bool: claimed(t, state))
  let more = if Relations.count(state.things) > 8n then "… and {natText(Relations.count(state.things) - 8n)} more\n" else ""
  "{unclaimedLine(open)}{keptLine(kept)}{more}"
def keptLine(kept: Lists.List<Plans.Reference>) -> String:
  if Lists.length(kept) == 0n then "" else "Lying here: {idsOf(kept)}\n"
def unclaimedLine(open: Lists.List<Plans.Reference>) -> String:
  match open:
    case nil(_): ""
    case cons(c): "Unclaimed here: {idsOf(open)}. Inspirit one: delvetalk {c.head.object} inspirit / surrounding: <1 to 400 characters>\n"
def idsOf(things: Lists.List<Plans.Reference>) -> String:
  textTake(textJoin(Lists.map(things, fn(t: Plans.Reference) -> String: t.object), ", "), 400n)
def presentLine(state: State, context: Abi.Context) -> String:
  if Relations.count(state.presence) == 0n then "" else "{if lapsed(state, context) then \"Last here, before it went quiet {natText((context.clock - state.tended) / 60n)} hours ago\" else \"Here\"}: {textJoin(Lists.map(Lists.take(Relations.rows(state.presence), 8n), fn(p: Presence) -> String: Card.shown(p.who, p.handle, context)), \", \")}{if Relations.count(state.presence) > 8n then \" and {natText(Relations.count(state.presence) - 8n)} more\" else \"\"}\n"
def headLine(state: State, context: Abi.Context, id: String) -> Document.Document:
  let count = "{title(state)} ({natText(if lapsed(state, context) then 0n else Relations.count(state.presence))} here)"
  let around = "{presentLine(state, context)}{lyingLine(state)}{if state.tone == \"\" then \"\" else Super.chimed(state, id)}"
  match here(context, state):
    case some(p): Document.text("{count}{passageText(p.value.at, state, context)}Say: delvetalk {id} say / line: <text>. Leave: delvetalk {id} leave.\n{around}")
    case none(_): Document.text("{count}. Enter: delvetalk {id} enter.\n{around}")
# The passage, its braces read for the reader (Spween.rendered), then the choices whose guards hold and the variables, as many as fit in what the
# card's room (Card.limit) leaves after the passage and the room's other lines (300 kept for them).
def passageText(at: String, state: State, context: Abi.Context) -> String:
  match passageAt(at, state):
    case none(_): ", you are here.\n"
    case some(ps): passageWith(ps.value, state, context, ", you are at {ps.value.id}:\n{Spween.rendered(ps.value.text, readerVars(context.principal, state), readerDraw(context, state))}\n{choicesHead(ps.value, state, context)}")
# A passage with no choice open keeps its reader; the owner of a scene of prose alone is shown the shape.
def choicesHead(ps: Passage, state: State, context: Abi.Context) -> String:
  match ps.choices:
    case cons(_): "Choices:\n"
    case nil(_): if context.principal == state.owner && Lists.length(state.passages) == 1n then "No choices: a reader stays here. A choice is * [Label] -> passage under the prose; the shape: delvetalk library read / page: spween; your scene replaced: delvetalk {Card.called(context, \"room\")} rescene\n" else "No choice is open here now; leave when you will.\n"
def passageWith(ps: Passage, state: State, context: Abi.Context, head: String) -> String:
  let choices = fitting(Lists.map(Lists.take(Lists.filter(ps.choices, fn(c: Choice) -> Bool: Scenes.holds(choiceGuard(c, state), readerVars(context.principal, state))), 8n), fn(c: Choice) -> String: choiceLine(extend(c, {guard: choiceGuard(c, state)}))), less(Card.limit(context), textLength(head) + 300n))
  let shown = Lists.concat(Lists.take(state.vars, 4n), Lists.take(Scenes.readerVars(Lists.List.nil(), ownOf(context.principal, state)), 4n))
  "{head}{choices}{fitting(Lists.map(shown, varLine), less(Card.limit(context), textLength(head) + textLength(choices) + 300n))}"
def less(room: Nat, used: Nat) -> Nat:
  if used >= room then 0n else room - used
def fitting(lines: Lists.List<String>, room: Nat) -> String:
  match lines:
    case nil(_): ""
    case cons(c): if textLength(c.head) > room then "" else "{c.head}{fitting(c.tail, room - textLength(c.head))}"
made
17 rows
  1. object
    room/1
    n
    1
    about
    empty
  2. object
    room/2
    n
    2
    about
    empty
  3. object
    room/3
    n
    3
    about
    empty
  4. object
    room/4
    n
    4
    about
    empty
  5. object
    room/5
    n
    5
    about
    empty
  6. object
    room/6
    n
    6
    about
    empty
  7. object
    room/7
    n
    7
    about
    empty
  8. object
    room/8
    n
    8
    about
    empty
  9. object
    room/9
    n
    9
    about
    empty
  10. object
    room/10
    n
    10
    about
    empty
  11. object
    room/11
    n
    11
    about
    empty
  12. object
    room/12
    n
    12
    about
    empty

and 5 more

count
17