Writing:Working the Example

Back to Dusty Hill. We can do much more with this world.

Moving around
Update the  of the location to look like this:

""

Now add a new property, called. Change its pop-up menu from  to.

The  property line now has four entries. For now, we’ll fill in just the  field; set it to   and leave the other three lines alone. Save this.

When you click on the “trail” link (in the game), you’ll see an error “No such location: hilltop”. Because, of course, there isn’t. Let’s fix this.

Add a second location. (Remember, you do this from the  world screen.) Give it the name , the key   (lower case!), and this description:

""

Now the “trail” link at the foot of the hill works – it brings you to the top.

However, the “trail” link at the top does not yet work. Remember, the properties you’ve created so far are attached to the  location; they’re not accessible from anywhere else. Add a new  property in the   screen to remedy this. The destination line should be.

To make life more interesting, add this text on the  line:

""

This message appears to the player (in the event pane) when he clicks the move link. To keep the balance, add an appropriate hill-climbing message to the  property in the   location.

The third and fourth lines of the move property are messages to other players; one to the players you are departing from, and one to the players you are heading towards. You don’t have to fill in these lines, because the system provides simple defaults. But you might want to provide a colorful customization, perhaps “[$name] trudges wearily up towards the distant summit.”

(As you’ve probably guessed,  is a magic token which is filled in with the acting player’s name. We’ll get to names and other pronoun magic later.)

Tidier hyperlinks
In my sample Ages, I’ve tried to make my travel hyperlinks longer, and include a clear description of the motion involved. For example:

""

Changing the text breaks the hyperlink, because the  property is still named. How to fix it?

We’d like to rename the property to “trail leads upwards”, but I said earlier that a property name cannot contain spaces. But try it anyway! You’ll find that the spaces are converted to underscores:. And indeed this works. By default, the text of a hyperlink is converted into a property name which is lowercase, with spaces and punctuation replaced by underscores.

(We call this “sluggification”, because... (blah blah blah long explanation...) Linotype type-casting machines. Never mind. The exact sluggification rules are a bit tricky. You can experiment if you like. The ultimate goal is to generate a valid Python identifier that looks like the original text.)

But what if we don’t want that long property name? It’s awkward, and prone to breakage if we happen to update the room description again.

Change the property name back to, and then update the   to this:

""

The vertical bar  indicates an explicit property name. The text before the bar is displayed to the player; the text after the bar is the property name to invoke:. No sluggification is performed here, so be sure to use lower-case after the bar.

Here’s another handy shortcut:

""

The double bar in  means that the whole text is displayed, but only the last part  is used to invoke the property. (The text after the double bar is sluggified.) This is a common pattern, when you want a long descriptive link but a short property name.

The linking page
Let’s go back to that “hazy destination” message, which is what appears if you look at the “Dusty Hill” entry in your linking booklet.

Add a new property in the  location, with the name   and the text:

""

is another special property. Add it to any location that you plan to use as a link-in spot. It is shown as the “linking page image.”

(A warning: unlike most text properties,  cannot use magic square-bracket tokens like   or the other interpolations we’ll talk about soon. If you try to put them in, they won’t behave magically; the player will see the square brackets as-is.)

Events
Not all actions involve walking around. At the top of the hill, set this description:

""

""

This is two paragraphs of text in the same property. Be sure to enter it that way! They must have a blank line between them, to mark a paragraph break – that is, you should hit Enter twice. (If you just hit Enter once, the paragraphs will flow together.)

Create a  property in this location. (Remember, the double bar  in the link text indicates that the whole string will be displayed to the player, but only last part names a property.)

Using the pop-up menu, change the type of the  property to. Now you see two text fields: a message to display to the player, and a message to display to other players in the same location. These messages will appear in the event pane. Try this line in the first field:

""

Click the link in the game to observe the effect.

Most events are visible to every nearby player. (Or, in this case, audible!) So it’s usually worth filling in the second “other players” field of the  property. Try this:

""

You won’t be able to test this message, since you’ve only got one character in the world. It does what you think, though.

We’ve seen the  token before. is a pronoun token; it gets filled in with “his”, “her”, “its” as appropriate. (Yes, it’s third-person singular. Use  if it really bugs you.)

Interpolation and assignment
Extend the second line of  to look like this:

""

(From now on, I’ll just be giving the second paragraph of the  property. The first paragraph, with the trail link, will stay the same. Be sure not to erase it, or you’ll be stuck on top of the hill...)

Note that  is in double square brackets. This token disappears in the game – the display reads “ people have been here,” which is hardly informative. That’s because it’s looking for a  property to insert, and we haven’t defined one.

Do that now; create a property called  whose value is. As soon as you save it, the location in the game will update to show the value: “0 people have been here.” Notice that double square brackets don’t produce a hyperlink; instead, they look up and insert the named property as-is. We call this “interpolation”, if we’re in a mood for three-bit words.

"No, you can’t insert a or   property into a room description this way. That would be silly. Only texts and other static values can be interpolated."

This isn’t an interesting example until the  property changes. You can have fun editing it in the build window, but really the point is for players to change it. So try this:

""

The word “chalk” is now in single brackets, which makes it a hyperlink. However, the target of the link (after the bar) isn’t a property; it’s a line of code. As you might expect, this increases the  property...

...Or not. If you try it, you’ll get a truculent TypeError.

The problem here is that the  property is text right now. The text string “0” is not a numeric 0. To fix this, change the type selector to, and fill in the 0 value. Now the “chalk” link works; you can click away, increasing the displayed value each time.

Notice, by the way, that the value in the build screen does not change when you click the link. The property is still defined as 0, even though it appears as 9 or 27 or some such exuberant value in the game. Why?

If you answered “property inheritance,” you understood more of that earlier chapter than I feared! The first time you clicked, the  statement read in the world value (zero), added one, and stored an instance value of 1. (The instance property is created as soon as a player action tries to store a property value.) Subsequent clicks increased this instance property.

If another player were standing in a different instance, she would still see the count as zero. And if she clicked the link, she would see her own instance-copy of the property increase.

The rule is: the build screen only shows the world definition, and player actions cannot change the world definition. Only the Writer can do that.

Conditional text
The chalk message is still quite primitive. Let’s fancy it up.

""

""

""

""

""

""

""

""

The new feature here is the  token, which lets us vary the text. In the first line, we’ve added “...and an eraser” (with another action link) but only if the  property exists and is not zero. The code of the eraser link is, which deletes the   instance property and allows the original world property (zero) to shine through. So the eraser link actually erases itself, which is sort of cool.

"The eraser link could have been instead. This doesn’t do exactly the same thing, but the result would look the same to the player."

The written message is a more complicated  sequence. Here we test an expression rather than a bare property.

For clarity, I’ve written the text out on multiple lines. Remember that adjacent lines of text get reassembled into a paragraph, so you can always use single line breaks to make your code easier to read. (A double line break – that is, a blank line – is displayed as a true paragraph break.)

Code
If the player uses the chalk, it should generate an event message as well as updating the  property.

It’s possible to jam multiple lines of code into a link, but the formatting gets ugly. Let’s use this opportunity to break the code out into a separate property.

""

""

""

""

""

""

""

""

Now create a  property, set its type to , and fill in these lines:

count = count+1 event("You grab the chalk and mark your presence on the tree.")

Clicking the link now increments the number and displays an event. The  function does what you think.

(If the link pops up a focus pane displaying the code, then you forgot to change the property type.  properties are displayed as focus close-ups; all other properties are executed.)

properties permit both a message to the acting player and a message to everybody else. You can do the same thing with the  function:

count = count+1 event("You grab the chalk and mark your presence on the tree.",   text("[$name] picks up the chalk and scrawls on the tree."))

The  function here ensures that   is interpreted as wiki text rather than a raw string. In a raw string, the  token would appear as-is to viewers. (We could apply  to the first argument too, but it’s not necessary, because there are no magic tokens there.)

I’ll leave the  property as an exercise.

One more example. We’ve seen that a link like  brings up a close-up view if   is a text property. You might conclude that, as a statement by itself, does this job, and you’d be right. You could instead write, and have a one-line   property:

sign

This accomplishes the same thing – popping up the  text property – in a more complicated way. Which looks silly, but you could extend the  property to do more:

event("You peer at the sign.", text("[$name] peers at the sign.")) sign

Code arguments
The code above displays “one person” when  is 1, but higher values appear as digits. Let‘s allow values up to ten to appear as English.

Create a property, and set its type to. The property will now act as a function with arguments.

The first field is the list of arguments; enter. This indicates that  takes one argument, which will be given the temporary internal name. (If there were multiple arguments, they would be separated by commas.)

In the second field, enter the following: _names = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten'] if num >= len(_names): return num else: return _names[num] (The entries ‘Zero’ and ‘One’ won't actually be used in this example, but by filling the appropriate spaces, they make sure the number names align properly with the array indexes.)

Finally, edit  by replacing   with   When this is displayed, the   property will be called with   as its argument.

Note that adding more number words is as easy as adding more entries to the end of the list – you don't need a separate elif for each case.

(This example is a bit artificial. We could have written  as an ordinary   property, checking the value of   directly. But by passing it as an argument, we’ve created a function which could be reused in several places in the Age.)

Timers
A rich environment may want to provide effects that happen “in the background” – either on a regular schedule, or as delayed reactions to player actions. Here is a simple example.

Go back to the foot of the hill. Add a second paragraph to, as follows:

""

The  property should be , containing these two lines:

event("You pump the handle hard, but only faint wheezing noises emerge.",   text("[$name] pumps the handle.")) sched(5, pumptimer)

This won’t work until you add a second  property, called  :

eventloc(locations.foot, "A few drops of water dribble from the pump.")

"Always schedule a code property, rather than a line of code. will not do what you want."

When you click the pump handle now, you’ll see one event immediately, and a second event five seconds later. The  function causes another code property to be invoked after a fixed delay. The delay is specified in seconds.

The  function is like , but it specifies the location where the event is displayed. ( triggers an event around the acting player, but there is no acting player in a timer event.)   only takes a single argument; everyone in the specified location will see the same event message.

We’ve also introduced the  notation, which specifies a location by its short name. You can also use this notation to access location properties. For example,  is the   property of the.

This brings up an important warning about delayed code, which is that it does not run in a particular location. When the player triggers the  property in this example, it runs in the player’s location, so   refers to a location property. But then when  runs, it’s floating around in space. There is no “acting player”, so  won’t work – that’s why we use. And there is no current location. Symbols refer to realm properties rather than location properties.

So if you want your delayed code to set a location property, you’d have to write. Writing just  would set the realm property.

"(Planned feature: properties should run in the location where they are defined. This would be a sort of “lexical scope”, and I think it would be less confusing overall, although the details are odd.)"

Repeating timers
You can also set up a repeating event:

sched(60, clockchime, repeat=True)

This invokes the  code property every sixty seconds, forever.

Except not actually forever. The server has a notion of which instances are “awake” – currently inhabited. Once every player has left an instance, the server waits ten minutes (in case someone comes back). After ten minutes uninhabited, the server puts the instance to “sleep”. All outstanding timers are cancelled. There will be no more activity in the instance until a player returns.

Usually you’ll want your repeating event to run all the time, as long as the instance is awake. You can set this up by creating a realm-level code property named. Put your  call in this, and it will be fired every time the instance is awakened.

A minor nuisance: if you set up an  property with the   line above, and then test it, you’ll have trouble editing the   property. Once the timer is running, changes to the  property will seem to have no effect – the code is already loaded into the timer.

To work around this, put the instance to sleep and wake it up again. Of course the ten-minute wait is tedious, so there’s a special command to do this immediately. Type  in the chat pane, and the server will immediately kick you out and put the instance to sleep.

(You can only do this in worlds that you created!)


 * The  property is one type of hook: a specially-named property that the server invokes in certain circumstances. There are several of these, which are listed in the Reference Section.

Creating a bookshelf
The most distinctive feature of Myst’s Ages are the linking books. We’ll certainly want a way to include links in our own worlds.

This is a little more complicated than adding an action or exit. It’s a three-step process: you must create a portal list (bookshelf), add portals (links) to the list, and then create a  property to display the list to the player.

Return to the world page in the build interface, and look at the “Portal lists” table. This shows all the bookshelves available in the world. (A standalone linking book is just a bookshelf with exactly one link.) Press the “New Portal List” button to create a new one.

You are now looking at an empty bookshelf. Set the key to. This string will be used later in a property to identify this list. (It’s rather like the key-name of a location.)

Now hit the “New Portal” button. You’ll get a new link... to the Information Booth in the starting world. Not very exciting! But this is just an initial placeholder.

Hit the “World” button on the left to change the link’s destination. The menu that appears will allow you to select any link you have in your personal booklet. The booklet is your “clipboard” for copying links around.

When you add a link this way, it copies the world, location, and scope from your linking booklet. So you may well wind up with a link to somebody else’s personal instance. You can stick with that, or change it. Press the “Instance” button to customize the destination instance.

The options here are a little confusing:


 * : The link will always go to the player’s own personal instance.
 * : The link will always go to the scope the player was in when they used it. If the player is in a global instance, the link will be to a global instance. If the player is in a personal instance, the link will be to the parallel personal instance. (Not necessarily the player’s personal instance!)
 * : The link will always go to the global instance. (“Player’s global” is a confusing label, sorry.)
 * : The link will always go to the global instance. (Just like the previous option. There’s a minor difference in the database entry but the result is identical. I should drop one of them.)
 * : The link will go to your personal instance (not the player’s!)

(The “Instance” button will be disabled if the target world has  or   instancing. By definition, a   world only has   available, and a   world only has  .)

"(Planned feature: the ability to select a friend’s personal scope, if that player has given you access.)" "(Planned feature: group scopes.)"

Once you have the list set up the way you want, go to a location in your world and create a  property. Set the key entry to, matching the list you just created. When the player looks at property, they’ll see your list.

The other lines of the  customize the behavior of the shelf. The read access level defaults to, meaning that anybody can look at the contents of the shelf. The edit access level defaults to, which currently means that only the instance owner can add new books to the shelf.

"The links you’ve added are part of the world definition, so only you can delete them. World-defined links appear in every instance. But the instance owner can add and remove additional links, which are specific to that instance."

The “Label” line will be displayed as a description of the bookshelf. You can use this to detail what the shelf looks like, or where it is in relation to the room. (This line may contain links.)

If you want to create a standalone book -- not part of a bookshelf -- you should create a list with exactly one link, and then set the property’s “Focus” line to. In this case, the “Label” line describes the book rather than the shelf. (But not the book’s linking image; that comes from the destination world’s  property.)

If you want to create a public bookshelf, which anybody can add books to, create an empty portal list. Then set up a  whose edit access is. (This is how the public shelf in the Seltani gallery works.)

Creating an age-wide booklet page
If you create a realm-level text property named, its contents will appear as a special booklet page (in the right column) for players in that world.

The  can contain links and interpolations, like any text property. You can use this facility to offer the player information and controls that are available throughout the Age.

Any code in the property will execute at the realm level; that is, there will be no "current location". The "current player" will be set correctly, but  will return. (If you need this value, use . Yes, this is an untoward nuisance.)

Note that the booklet page will only appear if the  exists and generates any text. So if you make the entire text conditional:

[$if condition]...[$end]

...then the booklet page will only appear when the condition is true. You might do this, to have a booklet page that only appears for privileged players:

[$if access.level(level=access.MEMBER)] ...   [$end]