Writing:Best Practices

From Seltanikor
Jump to navigationJump to search

Your First Age

Basic Concepts

Working the Example

The Ways of Printing

Assorted Features

Reference Section

The Scripting Language

Best Practices

Code Samples



Use proper curly quotes in text. Seltani’s body font (Sorts Mill Goudy) makes straight quotes look curly, and then they look unbalanced.

(This wiki also uses Sorts Mill Goudy. Note that the quotes on "this" look unbalanced.)


Use real dashes. The ASCII “dash” (technically a “hyphen-minus”) is angled in Sorts Mill Goudy, so using more than one of them in a row doesn't resemble a continuous longer line as it does in other typefaces.

  • En-dash: Instead of “--”, use “–”.
  • Em-dash: Instead of “---”, use “—”.


Hyperlinks for travel should clearly indicate the direction and usually the destination:

A flagstone path runs from the ruined pier [uphill towards the building].


A flagstone path runs from the ruined pier [uphill] towards the building.

(Planned feature: hyperlinks for inspecting, travel, and other action should have three different colors.)


Temporary Variables

Use temporary variables (beginning with an underscore) for loops:

   for _x in ls:

Temporary variables are never written to the database, so this runs faster. It also avoids leaving an instance property lying around after the loop.

Immutable Properties

Any property that you define in the build interface (world properties) are immutable. This doesn't matter for numbers and strings, which are immutable by definition in Python. But it's important for arrays and dicts. If you define a property

arr: (Value): [ 0, 0, 0 ]

in the build interface, then the statement

   arr[1] = 11

will fail. In fact it will fail silently. (The error is detected at an inconvenient point in the system, so it doesn't make its way back to the player's console.)

On the other hand, if you assign

   arr = [ 0, 0, 0 ]

in code, then you've created an instance property. Arrays and dicts in instance properties can be modified freely. So if you need a mutable array, the easiest plan is to assign it in your world's on_init hook:

on_init: (Code):

   location('locname').arr = [ 0, 0, 0 ]

Large Collection Properties

If you define an array or dict property (as in the previous section), you might think that it's efficient to update just one entry at a time.

Because of the way the database works, this is not true. The system reads the entire dict or array out of the database, modifies one entry, and then writes the entire thing back.

This doesn't mean you shouldn't do it. But be aware that it's not a time-saving technique.

If you want to store (potentially) lots of data efficiently, create a spare location and store separate properties in it:

   location('database')[key] = val

(The keys must be strings for this trick.)


Linking out and in

Remember that a player can link out at any time, from anywhere. But they can only link into locations you have created in-portals for. Don’t design a world that breaks if the player quits halfway and starts from the beginning.

Nested focus text

Nested focus text should be used sparingly.

Focus text is text in a popup description. Nested focus text is a description that is only reachable by clicking a link from another popup, as opposed to descriptions that are reachable directly from the main location description.

For example, suppose we have a location with the following description:

You are in a kitchen. There is a [stove] and a [cabinet] here.

Clicking stove pops up a focus pane with the following description:

The stove is an old electric model in pale green. There is a [cabinet] above the stove.

The cabinet link (in both places) pops up this:

The cabinet is dusty and bare. There are faint [scratches] in one corner.

Clicking scratches pops up the following:

In the back corner, someone has scratched the words “The password is swordfish.”

Of these four texts, the first is the location description and the other three are focus texts.

  • stove is available from the location description; it is not nested.
  • cabinet is available from either the location description or from stove; it is considered not nested because there is a link to it from the location description.
  • scratches is available only from cabinet; it is nested.

In this case, the nested scratches text is meant to be not immediately obvious just from looking around – the player has to search to find it. In this case, nesting makes sense. In general, however, information should not be nested if it would be obvious to someone who was really there.

Situational locations

(Or, the problem of a friend walking in on next Tuesday)

In the examples above, we’ve treated locations as places in a physical world – rooms where you could stand and talk to other players.

In theory, a “location” could be any kind of situation describable in text. Twine games frequently exploit this to create surreal choice-based narratives. Allow me to quote from an early Twine work, Mastaba Snoopy by gods17:

Yes. You fall through a trap door and soar ten billion miles down to a jagged pile of gross rocks.

Snoopy stands here, luxuriant, leaning against a red doghouse. His legs are spread wide open and you fight to avoid staring at his visibly throbbing crotch. One eyebrow raised. He knows what you’re thinking.

Sunglasses drip down his cheeks and splash onto his lips, puckered for a kiss from a cloud of chirping zambonis.

You are too injured to move anything but your eyes, so he tosses you a Heal potion. You thank him and walk away. It is not Snoopy that grins back, but Joe Cool...

This is text and it contains a hyperlink, but it’s not much like a room description.

More restrained examples might be “locations” that are pages in a journal, or days in a journey down a river. Belford does this in a subtle way in Leafspin; when you climb into a leaf, that’s a new location. Every position in the air is of course also a location, even though it makes no sense to imagine “standing” there or waiting any length of time.

So, if Leafspin does it, it must be okay, right? Sort of.

Here’s the problem. When several players inhabit a location, it grounds the physicality. No matter what the text says, you’re now a bunch of people standing around having a conversation. Time becomes real time, because you’re chatting with real people. Space is as far as you can comfortably see and speak.

Imagine this text:

You are too injured to move anything but your eyes, so he tosses you a Heal potion. You thank him and walk away. It is not Snoopy that grins back, but Joe Cool...

You see Fred and Steve here.

Fred says, “Sure is hot today.”

Anne arrives.

Steve says, “Eh, not so bad.”

I trust that demonstrates the problem.

So why does Leafspin work? Simple: Leafspin is a solo world, which means it is designed for solo exploration. You can only inhabit your own personal instance of Leafspin; you can never meet another player there. The problem never arises!

Therefore, this rule: if you plan to make situational locations, set the world’s instancing model to solo. You are then free to play around with space and time however you like.

So, for example, the various pamphlets on the Seltani info table are not separate locations, because the Seltani District is a shared world. The table is a physical location. Several players could stand there, reading different pamphlets – or the same pamphlet – and simultaneously chatting with each other. This is why Seltani’s UI is designed with separate locale and focus panes.

Will this rule be enforced? No. How you construct your world is up to you. However, Belford has built Seltani (and his Ages) to offer a degree of realism – a world that players can become immersed in. Allowing players to co-exist in a non-physical situation breaks the sense of immersion.