Working With...Exits & Doors
Exits connect Rooms to each other. Exits are one-way, but can be paired with a second exit to make a two-way connection. Exits lack physical properties, but can be linked with an Aperture (aka a Door or Window) to add physical properties that react to verbs like unlock and open. There is a shorthand method and a longhand method for setting exits. Which one you use mostly depends on how much control you want over its behavior and appearance.
Demo Game
// WorkingWithExits.js
/* global AdventureJS A Game */
var WorkingWithExits = new AdventureJS.Game(
"WorkingWithExits",
"WorkingWithExitsContainer"
);
WorkingWithExits.settings.set({
display: "inline",
layout: "classic",
title: "Working With Exits",
author: "Ivan Cockrum",
description: "Today we are working with exits. ",
version: "0.0.1",
//
print_exits_in_room_descriptions: true,
print_exits_when_travel_fails: true,
print_exits_room_names: true,
print_exits_room_names_if_known: true,
print_exits_room_names_if_used: true,
print_named_exits: true,
print_exits_links: true,
prints_exits_links_around_custom_labels: false,
});
WorkingWithExits.createAsset({
class: "Player",
name: "Explorer",
place: { in: "Start Room" },
});
// --------------------------------------------------
// Rooms
// --------------------------------------------------
WorkingWithExits.createAsset({
class: "Room",
name: "Start Room",
definite_article: "the",
description: `This demo features examples of several exit features: exits defined via the shorthand and longhand methods; false exits; named exits; custom exit labels; assigning keys to locked doors; and destroying a key after use. You can find code for all these examples in the game's <a href="/downloads/WorkingWithExits.zip">source files</a>. `,
exits: {
// SHORTCUT METHOD
east: "East Room",
west: "West Room",
// for the north room we're going
// to use the longhand method instead
// find that in the EXITS section
// north: "North Room",
// for the south room, we're going to use the longhand
// method so we can set up a custom label
// find that in the EXITS section
// south: "South Room",
// FALSE EXITS
up: "Up, up and awaaaay, in my beautiful, my beautiful baloooon!",
down: "You kneel to examine the ground but find no way to travel. ",
in: "Curling up somewhere cozy sounds appealing, but you don't see anything to get into. ",
out: "There is...No Way Out. Brrr. ",
// NAMED EXIT
workshop: "Workshop",
},
});
WorkingWithExits.createAsset({
class: "Room",
name: "North Room",
definite_article: "the",
description: `This is the North Room. `,
exits: {
// we're going to use the longhand method for this south door
// south: "Start Room",
},
});
WorkingWithExits.createAsset({
class: "Room",
name: "South Room",
definite_article: "the",
description: `This is the South Room. `,
exits: {
north: "Start Room",
},
});
WorkingWithExits.createAsset({
class: "Room",
name: "East Room",
definite_article: "the",
description: `This is the East Room. `,
exits: {
west: "Start Room",
},
});
WorkingWithExits.createAsset({
class: "Room",
name: "West Room",
definite_article: "the",
description: `This is the West Room. `,
exits: {
east: "Start Room",
},
});
WorkingWithExits.createAsset({
class: "Room",
name: "Workshop",
definite_article: "the",
description: `This is the Workshop. `,
exits: {
// NAMED EXIT
"Start Room": "Start Room",
},
});
// --------------------------------------------------
// Exits
// --------------------------------------------------
WorkingWithExits.createAsset({
class: "Exit",
direction: "north",
place: { in: "Start Room" },
destination: "North Room",
descriptions: {
look: "It looks frosty that way. ",
// label: "north to the <span class='exit-destination'>North Room</span>",
},
aperture: "icy door",
});
WorkingWithExits.createAsset({
class: "Exit",
direction: "south",
place: { in: "North Room" },
destination: "Start Room",
descriptions: {
look: "It looks standy that way. ",
label: "south to the <span class='exit-destination'>Start Room</span>",
},
aperture: "icier door",
});
WorkingWithExits.createAsset({
class: "Exit",
direction: "south",
place: { in: "Start Room" },
destination: "South Room",
descriptions: {
look: "You see a way south. ",
// CUSTOM LABEL
label:
"<a>south</a> to the warm, toasty looking <span class='exit-destination'>South Room</span>",
},
});
// --------------------------------------------------
// Doors
// --------------------------------------------------
WorkingWithExits.createAsset({
class: "Door",
name: "icy door",
other_side: "icier door",
is: { closed: true, locked: true }, // set is.locked
adjectives: "icy, north",
synonyms: ["icy north door"],
description: "The icy north door is { icy door [is] open [or] closed }.",
// dov = Direct Object Verb subscription
dov: {
unlock: { with_assets: ["glass key"] },
lock: { with_assets: ["glass key"] },
},
});
WorkingWithExits.createAsset({
class: "Door",
name: "icier door",
other_side: "icy door",
synonyms: ["icier south door"],
adjectives: "icier, south",
description:
"The icier door is very slightly translucent. You can't see through it, but it has a bit of a glow. ",
});
// --------------------------------------------------
// Objects
// --------------------------------------------------
WorkingWithExits.createAsset({
class: "Key",
name: "glass key",
article: "a",
adjectives: "",
place: { in: "Start Room" },
// iov = Indirect Object Verb subscription
iov: {
unlock: {
// DESTROY KEY
// we're setting this key to destroy
// itself upon use, just 'cuz
then_destroy: true,
on_destroy: "The glass key shatters into pieces. ",
},
},
}); // glass Key
Demo Source files
Right-click and choose "Save as..." to download source files.
Notes:
- Demo games are set to a small size to fit doc pages. They can easily be enlarged via display settings.
- Demo games all use the same theme. Colors and fonts can be changed via custom CSS.
- Demo games don't include the core AdventureJS code. To run downloaded games locally, you'll need to also download copies of the core JS and CSS and set up this local folder structure.
AdventureJS └── js | └── adventure.min.js └── css | └── adventurejs.min.css └── games ├── demogame01 ├── demogame02 └── demogame03
Shorthand Method
The shorthand method creates exits with default settings. Here is all it takes to create an exit. If you've created the rooms that these exits lead to, the rest will be handled automatically during the game's initialization.
MyGame.createAsset({
class: "Room",
name: "Start Room",
description: "Welcome to the Start Room! ",
exits: {
east: "End Room", // we call this a key/value pair
},
});
MyGame.createAsset({
class: "Room",
name: "End Room",
description: "You've reached the End Room! ",
exits: {
west: "Start Room",
},
});
To say this another way, we're creating an exits object on our
room object, and then assigning it key/value pairs where the
key is a compass direction and the value is the name of a room.
Named Exits
Named exits are defined using room names instead of compass directions. Named exits can be used to join rooms that aren't necessarily contiguous. You can think of them as "go place" rather than "go direction". For example, perhaps you have a kitchen and a pantry, and you want the player to be able to travel from the kitchen to the pantry just by typing pantry. Players can use named exits by typing just the room name. All of these work: pantry, go pantry, go to pantry.
MyGame.createAsset({
class: "Room",
name: "Start Room",
description: "Welcome to the Start Room! ",
exits: {
east: "End Room",
workshop: "Workshop",
},
});
Apart from the name vs direction distinction, named exits are otherwise the same as directional exits. If you wanted to, you could make all your exits this way without any problems, and have a game where you navigate by room names instead of compass directions.
There is one bit of nuance to know about. Putting named exits aside for a moment, AdventureJS always accepts a command like go to Start Room, and, depending on game settings and player knowledge, the parser will find a route from the player's location to the requested location, moving the player through each location en route. That is different from using named exits, which are like wormholes or portals that always go directly from one room to another without any travel in between. You could, if you wanted, use named exits as actual portals, connecting points that are far away from each other on a map.
False Exits
False exits are exits that don't lead anywhere, but just print an author's
message if a player tries to use them. False exits can be made using the
shorthand method, simply by providing a text string instead of a room name.
If a player tries to use that exit, the game will print the text instead of
treating it as an exit. This is an easy way to add customized
"you can't go that way" sort of messages to any exit. If the
print_exits_when_travel_fails setting is true, the game will
print a list of available exits after the player tries a fake exit.
MyGame.createAsset({
class: "Room",
name: "Start Room",
description: "Welcome to the Start Room! ",
exits: {
east: "End Room",
up: "You flap your arms, but remain steadfastly on the ground. ",
down: "You kneel to examine the ground but find no exit there. ",
},
});
You flap your arms, but remain steadfastly on the ground.
Longhand Method
The longhand method gives you more control over the exit. We're keeping it simple in this example, so the exits created by this example code will look the same as exits created using the shorthand method.
MyGame.createAsset({
class: "Room",
name: "Start Room",
description: "Welcome to the Start Room! ",
});
MyGame.createAsset({
class: "Exit",
direction: "north",
place: { in: "Start Room" },
destination: "North Room",
description: "It looks frosty that way. ",
});
MyGame.createAsset({
class: "Room",
name: "North Room",
description: "Brrr! It sure is cold here in the North Room. ",
});
MyGame.createAsset({
class: "Exit",
direction: "south",
place: { in: "North Room" },
destination: "Start Room",
description: "That way leads back to the start room. ",
});
You might have noticed that we haven't given these exits any names, which makes them unique among other asset types. Most asset classes are expected to be given a name, which is converted to an ID. Exits aren't given names, and their ID becomes the name of the room they're in plus the direction they lead. So in this example our exit assets will receive the IDs start_room_north and north_room_south. We do this to ensure that all exits have unique IDs in the world lookup, and prevent exits having duplicate IDs.
Linking Apertures
By themselves, Exits don't have any state. That is to say, they're not open
or closed, locked or unlocked; they're just portals to other rooms.
Apertures are a separate
class that provides physical characteristics and states for Exits: doors
that open and close, lock and unlock. Apertures don't have to be doors: they
can be windows, or manhole covers, or a hole in the ground, or a glowing
portal. An Aperture can have another Aperture set as its
other_side, and both sides will be automatically kept in sync:
open one and the other is opened too.
MyGame.createAsset({
class: "Room",
name: "Start Room",
description: "Welcome to the Start Room! ",
});
MyGame.createAsset({
class: "Exit",
direction: "north",
place: { in: "Start Room" },
destination: "North Room",
aperture: "icy door",
description: "It looks frosty that way. ",
});
MyGame.createAsset({
class: "Door",
name: "icy door",
other_side: "icier door",
adjectives: "icy, north",
synonyms: ["icy north door"],
is: { closed: true },
description: "The icy north door is { icy door [is] open [or] closed }.",
});
MyGame.createAsset({
class: "Room",
name: "North Room",
description: "Brrr! It sure is cold here in the North Room. ",
});
MyGame.createAsset({
class: "Exit",
direction: "south",
place: { in: "North Room" },
destination: "Start Room",
aperture: "icier door",
description: "That way leads back to the Start Room. ",
});
MyGame.createAsset({
class: "Door",
name: "icier door",
other_side: "icy door",
synonyms: ["icier south door"],
adjectives: "icier, south",
description: "The icier door is very slightly translucent. You can't see through it, but it has a bit of a glow. ",
});
Unlocking with Keys
Doors can be opened without a key unless set otherwise. The way to set a
door to require a key is via a
verb subscription. First,
we'll add a new key asset. Then, we'll configure the door's
unlock verb subscription to accept the key.
MyGame.createAsset({
class: "Room",
name: "Start Room",
description: "Welcome to the Start Room! ",
});
MyGame.createAsset({
class: "Exit",
direction: "north",
place: { in: "Start Room" },
destination: "North Room",
description: "It looks frosty that way. ",
aperture: "icy door",
});
MyGame.createAsset({
class: "Door",
name: "icy door",
other_side: "icier door",
is: { closed: true, locked: true }, // set is.locked
adjectives: "icy, north",
synonyms: ["icy north door"],
description: "The icy north door is { icy door [is] open [or] closed }.",
dov: {
// dov = Direct Object Verb subscription
unlock: { with_assets: ["glass key"] },
lock: { with_assets: ["glass key"] },
},
});
MyGame.createAsset({
class: "Key",
name: "glass key",
article: "a",
place: { in: "Explorer" },
});
This also works with classes. Let's say your game had a variety of skeleton
keys. We can set the door to be unlockable with any asset of class
SkeletonKey.
MyGame.createAsset({
class: "Door",
name: "icy door",
other_side: "icier door",
is: { closed: true, locked: true },
adjectives: "icy, north",
synonyms: ["icy north door"],
description: "The icy north door is { icy door [is] open [or] closed }.",
dov: { // Direct Object Verb subscription
unlock: { with_classes: ["SkeletonKey"] },
lock: { with_classes: ["SkeletonKey"] },
},
});
Custom Labels
By default, exits are listed by their directions, as in this example.
If settings.print_exits_room_names is true, exits will be
printed with the names of the rooms they lead to. There are also further
options to control whether room names are shown depending on player
knowledge. These settings can be applied globally or per room.
Exit labels can be further customized by setting an
exit.descriptions.label property. When this label description
is present, it overrides the default label.
WorkingWithExits.createAsset({
class: "Exit",
direction: "south",
place: { in: "Start Room" },
destination: "South Room",
descriptions: {
look: "You see a way south. ",
// CUSTOM LABEL
label: "south to the warm, toasty looking South Room",
},
});
Clickable Exits
In the next block, we're going to get into global settings to control how
exits are presented. But before we get there, we want to callout two
particular settings that present a bit of aesthetic nuance. We're talking
about the
print_exits_links setting, which turns directions into
clickable links (aka
anchor tags).
MyGame.settings.set({
print_exits_links: true,
});
Now, let's say that you want to write some custom labels, as shown in the previous block.
MyGame.createAsset({
class: "Exit",
direction: "south",
place: { in: "Start Room" },
destination: "South Room",
descriptions: {
look: "You see a way south. ",
// CUSTOM LABEL
label: "south to the warm, toasty looking South Room",
},
});
By default, print_exits_links doesn't apply to custom labels,
resulting in something like this.
So we wind up without a link on south. A second option,
prints_exits_links_around_custom_labels can solve this.
MyGame.settings.set({
prints_exits_links: true,
prints_exits_links_around_custom_labels: true,
});
But, depending on your personal aesthetics, you may or may not appreciate
having the entire phrase turned into a link. To avoid linking the entire
text, instead of enabling
prints_exits_links_around_custom_labels, you can use simple
<a> tags in your labels.
MyGame.settings.set({
prints_exits_links: true,
prints_exits_links_around_custom_labels: false,
});
MyGame.createAsset({
class: "Exit",
direction: "south",
place: { in: "Start Room" },
destination: "South Room",
descriptions: {
look: "You see a way south. ",
// CUSTOM LABEL
label: "<a>south</a> to the warm, toasty looking South Room",
},
});
Just the <a> tag. Don't add a full URL or javascript. The parser will handle the rest, resulting in something like this.
Exits Settings
Most exit settings are available per room and globally for the game. Per room settings will always override global game settings, so you can set one thing globally and then change it up per room.
- room.print_exits_in_room_descriptions game.settings.print_exits_in_room_descriptions {Boolean} If true, a list of exits will be appended to room descriptions.
- room.print_exits_when_travel_fails game.settings.print_exits_when_travel_fails {Boolean} If true, when player tries to go in a direction that hasn't got an exit, print the current room's exits as a reminder of what exits are available.
- room.print_exits_room_namesgame.settings.print_exits_room_names {Boolean} If true, exit descriptions can include the name of the room that the exit leads to. (The logic for this may also consider other conditions such as whether the player knows about the other room.)
- room.print_exits_room_names_if_known game.settings.print_exits_room_names_if_known {Boolean} If true, exit descriptions will only include the name of the room that the exit leads to if the player knows about the destination room. (Generally a player must visit a room to know about it, but there exceptions are possible.)
- room.print_exits_room_names_if_used game.settings.print_exits_room_names_if_used {Boolean} If true, exit descriptions will only include the name of the room that the exit leads to if the player has previously used the exit.
- room.print_named_exitsgame.settings.print_named_exits {Boolean} If true, exit lists will include named exits. Named exits use another room's name instead of a compass direction. This setting defaults to false, normally excluding named exits from lists of room exits.
- game.settings.print_exits_links {Boolean} If true, exit descriptions can include hyperlinks that the player can click to go in that direction. This setting is only global, not per room.
- game.settings.prints_exits_links_around_custom_labels {Boolean} If true, custom exit labels will be wrapped with hyperlinks that the player can click to go in the direction of the exit. This setting is only global, not per room.
Further Reference
- Create an Exit in Getting Started
- Aperture class in Reference Manual
- Door class in Reference Manual
- Exit class in Reference Manual
- Hole class in Reference Manual
- Manhole class in Reference Manual
- Window class in Reference Manual
- Working with Scenery
- Scenery exits in Reference Manual
- Verb subscriptions in Author's Manual