This whole page is an illustration. It shows one shopkeeper's dialogue used by many shopkeepers, with hundreds of lines chosen by many variables. The spelling of dialogue itself (dialogue, line, choice(..., then:)) is Text's illustration, not a chosen form. Every name and number here is made up.

What the shopkeepers are

Imagine a small market by the harbor: three stalls under awnings, a baker, a potter and a fishmonger, each standing behind a counter. Walk up to one and speak, and a panel opens over the middle of your picture, the scene behind it darkened. The shopkeeper greets you: in the evening "Evening. The lamps are lit.", in the rain "Come in out of the rain.", the first time "Welcome to Ada's bakery.", and if you are a regular "Back again!". Then a line of patter, a different one each day, shared by every shopkeeper in the market: "They say the brook runs high this week." Below, one button for each thing the stall sells, with its price ("A loaf, 3 coins"); a button to haggle; and "Goodbye". Buy something and the shopkeeper says "There you are." over the counter for everyone near to see, and remembers you bought. Haggle once: a stranger is told "Come back when I know you."; someone who has bought three times or more gets a tenth off from then on; a second haggle gets "That's my last word." The three shopkeepers say the same kinds of thing in the same way, but each has its own name, its own wares and its own prices: the potter's are dearer than the baker's.

The listing

// Everything on this page is an illustration.
struct ware {
  name:  text;
  price: coins;
}

// ===== The shopkeeper: one design, several shops =====
part shopkeeper
  field shop    : text                                     // own: "Ada's bakery"
  field wares   : List[ware]                               // own
  field markup  : number = 1.2, within: 1 ..= 1.5          // own: how dear this stall is
  field patter  : List[text] = ["They say the brook runs high this week.",
                                "The balloon went up twice yesterday.",
                                "Mind the lanterns on your way home."], common: true   // every shopkeeper's
  field custom  : Table[being, number] = 0, mutable: true, lives: server   // own: how often each being has bought here
  field haggled : Table[being, bool] = false, mutable: true, lives: server // own: whether each being has haggled here
  part body: people.averageWoman
    being(self)

  fn priceOf(w: ware, v: being) coins {
    let base = w.price * markup;
    if custom[v] >= 3 && haggled[v] { base * 0.9 } else { base }
  }

  fn greeting(v: being) text {
    if lampLevel > 0 { "Evening. The lamps are lit." }
    else if weather.rain > 0.5 { "Come in out of the rain." }
    else if custom[v] == 0 {
      var words = "Welcome to ";
      words <- shop, ".";
      words
    }
    else { "Back again!" }
  }

  fn offerText(w: ware, p: coins) text {                   // "A loaf, 3 coins"
    var words = w.name;
    words <- ", ", p;
    words
  }

  fn talk(v: being) dialogue {
    let buying = List[choice]() <- each w in wares yield
                   choice(offerText(w, priceOf(w, v)), then: send(buy(w, by: v), to: self));
    dialogue(List[dialogueItem]() <-
      line(greeting(v)),
      line(patter[dayOf(now(world)) % patter.count]),
      each buying,
      choice("Can you do better on the price?", then: send(haggle(by: v), to: self)),
      choice("Goodbye.", then: say(self, "Mind how you go.", to: v)))
  }

  on(action(speak, to: self, by: v), present(talk(v), to: v))

  on(message(buy, w, by: v), {
    set(custom[v], custom[v] + 1);
    say(self, "There you are.", to: v);
  })
  on(message(haggle, by: v), {
    if haggled[v] {
      say(self, "That's my last word.", to: v);
    } else if custom[v] >= 3 {
      set(haggled[v], true);
      say(self, "For you, a tenth off.", to: v);
    } else {
      set(haggled[v], true);
      say(self, "Come back when I know you.", to: v);
    }
  })

// ===== The market: three copies =====
part market, at: (-40, land.height((-40, 0, 470)), 470)
  part baker      : shopkeeper(shop: "Ada's bakery", markup: 1.1,
                      wares: [ware(name: "A loaf", price: 3 coins), ware(name: "A honey cake", price: 5 coins)]),
                    at: (0, 0, 0)
  part potter     : shopkeeper(shop: "Bran's pots", markup: 1.4,
                      wares: [ware(name: "A jug", price: 12 coins), ware(name: "A bowl", price: 8 coins)]),
                    at: (6, 0, 0)
  part fishmonger : shopkeeper(shop: "The harbor catch",
                      wares: [ware(name: "Two mackerel", price: 4 coins)]),
                    at: (12, 0, 0)

// ===== The world's dialogue panel: how this world shows a presented dialogue =====
part talkPanel
  field with    : ?being = None, mutable: true, lives: client      // whom the panel is talking to, if anyone
  field showing : dialogue = dialogue([]), mutable: true, lives: client
  field picture : image
  derive(picture, layout(
    box([column(List[element]() <-
            each l in linesOf(showing) yield text(l, wrap: 364 px),
            each c in choicesOf(showing) yield button(c.words, does: choose(c)),
          style: styles.dialog, gap: 8 px, padding: 18 px, size: (400 px, auto),
          anchor: (0.5, 0.5), pivot: (0.5, 0.5))],
        style: styles.scrim, anchor: (0, 0, 1, 1), modal: true, visible: with != None)))
  on(presented(d, by: s, to: you), {
    set(showing, d);
    set(with, Some(s));
  })
  on(action(choose, c, by: you), set(with, None))

// ===== A choice comes back as an action the world answers =====
on(action(choose, c, by: v), perform(c.then))

Walking through it

One design, common and own fields

shopkeeper is a design. Its own fields differ per stall: the shop's name, its wares (a list of ware records, Values: records), its markup. patter is common: true: one list, held by the design, read by every copy; change it and every shopkeeper in the market has new gossip. custom and haggled are tables keyed by being, own to each stall, so buying at the baker's makes you a regular there and nowhere else. They have lives: server: a visitor's machine cannot claim a discount (Visitors: nothing a visitor can forge). The market places three copies, each stating only its differences; the fishmonger keeps the default markup.

Lines chosen by variables

greeting and priceOf are functions inside the design, seeing its fields by their bare names. The greeting is chosen by four variables in order: the world's lamp level (evening), the weather's rain, whether this being has bought here, and otherwise the regular's line (Control flow: if). The first-visit line joins the shop's name into the text with Cone's <- on a mutable local, allowed in a value block because a local is not the world (Text: joining). The day's patter is chosen by the day number, the remainder after dividing by the list's length.

Structured text with choices

talk returns a value of type dialogue (Text: structured text): lines the shopkeeper says, and choices a visitor may pick, each carrying the act it will perform. Building it does nothing: the acts are values, as an animator is built and only runs when started. The buying choices are made by a loop that builds a list (Control flow: each), one per ware with its price for this visitor; the dialogue's own list takes them in place with each buying, between the lines and the last two choices. Buying and haggling are sends back to the shopkeeper (Actions: send), so the shopkeeper's own handlers decide what they mean; leaving is a say.

Presented, shown and chosen

Speaking to a shopkeeper is the world's own action speak, aimed at a shopkeeper (Visitors: actions). The handler presents the dialogue to the visitor. Presenting is an act, like speech: it crosses the network to the visitor's machine, and the world's rule there shows it (Overlays: dialogues). This world shows it as a modal panel: a scrim darkening the picture, a column 400 px wide in the middle with the lines and one button per choice. Each button's does: names the visitor's action choose, carrying the choice: a press is the visitor's act, checked by the server like any other (Layouts: controls). The last rule answers that action on the server by performing the act the choice carried. The two handlers on the shopkeeper then change its tables and say their line; every visitor near sees "There you are." over the counter by the world's speech rule (as in the riddle).

← A boss and its army | Contents | Open questions →