Pre-release
AdventureJS Docs Downloads Bundler Devlog Score: 0 Moves: 0
Tutorial explaining how to work with rooms in AdventureJS. tutorial, rooms WorkingWithRooms styles

Working With...Rooms

Rooms are the main containers of all tangible things. A game must have at least one room. If no rooms are defined, a default one will be created automatically and the player placed in it. Every object in the game world, including the player, exists in a room (or otherwise in limbo, outside the world). Rooms can have exits and doors to other rooms. Rooms can be substance reservoirs. Rooms can limit the capabilities of a player within them. Rooms can subscribe to zones and share their scenery and events.

 

Demo Game

// WorkingWithRooms.js
/* global AdventureJS A WorkingWithRooms G */

var G = (WorkingWithRooms = new AdventureJS.Game(
  "WorkingWithRooms",
  "WorkingWithRoomsContainer"
));

// --------------------------------------------------
// Settings
// --------------------------------------------------

G.settings.set({
  display: "inline",
  layout: "classic",
  title: "Working With Rooms",
  author: "Ivan Cockrum",
  description: "Today we are Working With Rooms",
  version: "0.0.1",

  // This setting, if true, prevents exit destinations
  // being printed until player has used the exit.
  // It's true by default, but we just want to show
  // destinations for this demo.
  print_exits_room_names_if_used: false,

  debug: {
    general: true,
  },
  log: {
    all: true,
  },
});

// --------------------------------------------------
// Player
// --------------------------------------------------

var player = G.createAsset({
  class: "Player",
  name: "Explorer",
  place: { in: "Start Room" },
  can: { swim: true },
});

// --------------------------------------------------
// Rooms - Start Room
// --------------------------------------------------

G.createAsset({
  class: "Room",
  name: "Start Room",
  description: `
    Welcome to the Working With Rooms demo. 
    Here you will find various techniques 
    for dressing up a game's environment.
    You'll find walls to the east, 
    reservoirs to the south, scenery to the 
    north, and a false exit up.

  `,
  exits: {
    // This is shortcut method for creating an exit.
    north: "Mountain Forest",
    south: "Sandy Beach",
    east: "Walled Room",

    // This is a false exit.
    // We're not creating an actual exit,
    // but just providing a string to print
    // to the player if they try to go that way.
    // if game.settings.print_exits_when_travel_fails
    // is true (and it is true by default),
    // a list of actual exits will be
    // appended to this text.
    up: `
      Though {we} feel a strong urge to climb, 
      there is no way up from here.
    `,
  },
});

// --------------------------------------------------
// Rooms - Mountain Forest
// --------------------------------------------------

G.createAsset({
  class: "Room",
  name: "Mountain Forest",
  description: `
    Welcome to the Mountain Forest. A mountain slope rises above {us}. 
    Evergreen trees climb the slope. Tiny white aphids dance in rays of 
    light between the trees. A few early fireflies spark in areas of deeper shadow.
  `,
  definite_article: "the",
  exits: {
    south: "Start Room",
  },
  // Use area_scenery to take advantage of built-in phenomena
  // or create your own custom scenery.
  area_scenery: {
    // Trees are a built-in phenomena
    // and we only need to add a description for this room.
    trees: {
      enabled: true,
      description:
        "A mixture of evergreen and conifer trees fill the mountain slope. ",
      synonyms: "evergreen, conifer",
    },

    // Mountain is also built-in
    mountain: {
      // for built-ins, it's necessary to specify enabled: true
      enabled: true,
      // This description only applies to this room.
      // We can use mountains elsewhere with different descriptions.
      description: "The mountain slope rises impassably above {us}. ",
      // We can add synonyms and adjectives to built-in scenery.
      synonyms: "slope",
    },

    // If there's one mountain, player might use "mountains", so...
    mountains: {
      enabled: true,
      description: "The range of mountains extend to the ends of sight. ",
    },

    // CUSTOM SCENERY
    // We're also going to make a new custom scenery object.
    aphids: {
      // enabled: true, // not necessary for one-offs
      adjectives: ["woolly", "ash", "tiny", "white", "smoky-winged"],
      synonyms: ["cloud", "clouds"],
      description: `
        Clouds of tiny, white, smoky-winged ash aphids 
        drift gently among the trees, occasionally catching rays 
        of afternoon sunlight. 
      `,
    },
  },
});

G.createAsset({
  class: "Scenery",
  name: "fireflies",
  description: `
    A rare few fireflies spark dimly in the shadows 
    beneath some of the older trees. 
  `,
  place: { in: "Mountain Forest" },
});

// --------------------------------------------------
// Rooms - Walled Room
// --------------------------------------------------

G.createAsset({
  class: "Room",
  name: "Walled Room",
  definite_article: "the",
  description: `
    Welcome to the Walled Room. This room uses 
    <code>area_scenery</code> to place walls.
    Look at the cardinal walls: 
    east wall, west wall, north wall, south wall. 
  `,
  exits: {
    west: "Start Room",
  },
  // Scenery walls are a built-in feature.
  // They are global scenery objects which can
  // be given different descriptions per room.
  area_scenery: {
    "east wall": {
      enabled: true,
      description:
        "The east wall is dominated by a very large, ornate portrait. ",
    },
    "west wall": {
      enabled: true,
      description: "The west wall is broken by a passage west. ",
    },
    "north wall": {
      enabled: true,
      description: "The north wall is tiled in bright arabic patterns. ",
    },
    "south wall": {
      enabled: true,
      description: "The south wall hides a terrible secret. ",
    },
  },
});

G.createAsset({
  class: "Scenery",
  name: "stuff",
  synonyms: ["portrait", "pattern", "secret"],
  adjectives: ["ornate ", "large", "bright", "terrible", "arabic"],
  description: `
    None of the stuff on the walls is actually represented 
    here, because that's not the point of the demo. 
    But in a real game, you could easily add assets 
    for any of the items described with the walls.
  `,
  place: { in: "Walled Room" },
});

// --------------------------------------------------
// Rooms - Sandy Beach
// --------------------------------------------------

G.createAsset({
  class: "Room",
  name: "Sandy Beach",
  description: `Welcome to the Sandy Beach. The inland ocean extends to the far horizon. `,
  definite_article: "the",
  exits: {
    north: "Start Room",
    south: "Inland Ocean",
    east: "Eastern Beach",
  },
  contains: "sand",
});

// AdventureJS does not create substances by default,
// but this is all it takes to define a substance.
G.createAsset({
  class: "Solid",
  name: "sand",
  description: "It's a coarse 60-grit sand. ",
});

// TAKE WATER - SOLUTION v1
// This ocean view is a bit tricky. Logically,
// someone standing on a beach can reasonably
// expect to have access to both sand and water.
// The Sandy Beach room has a sand reservoir,
// from which players can "get sand".
// So that's covered.
//
// This is where it gets complicated. We know that
// there's an ocean, but in order to refer to it,
// we need an ocean asset. Normally we would just use
// a piece of scenery, but the player should be able
// to "get water", so our ocean asset needs a water
// reservoir. We'll use a Waterbody asset.
//
// It gets further complicated when you consider that
// player may want to say "enter ocean" or "swim in ocean".
// Normally this would be the job of an exit, but our
// ocean asset isn't an exit...  so we're going to add
// a teleporter to it. Anything a player can enter
// (go in, on, under, behind) can be turned into
// a teleporter by setting the asset's nest.target property.
//
G.createAsset({
  class: "Waterbody",
  id: "ocean view",
  name: "ocean",
  synonyms: "ocean",
  place: { in: "Sandy Beach" },
  description: `
    The inland ocean laps gently against the sandy beach, 
    and extends to the far horizon. 
  `,

  // contains is a shortcut method for setting an asset
  // vessel with an infinite quantity of substance
  contains: "salt water",

  // We're using the nest shorthand to set the target
  // property of a nest, which turns that nest into
  // a teleporter. If the player tries to enter this asset,
  // they will be moved to the target location.
  nests: {
    in_target: "Inland Ocean",
  },

  // doPlayerEnter: function () {
  //   console.warn(`try to movePlayerTo("Inland Ocean")`);
  //   G.movePlayerTo("Inland Ocean");
  //   return true;
  // },
});

// AdventureJS does not create substances by default,
// but this is all it takes to define a substance.
G.createAsset({
  class: "Liquid",
  name: "salt water",
  synonyms: "water",
  description: "It's salty. It's watery. It's salt water. ",
});

// --------------------------------------------------
// Rooms - Inland Ocean
// --------------------------------------------------

G.createAsset({
  class: "Room",
  name: "Inland Ocean",
  description: `Welcome to the Inland Ocean. `,
  definite_article: "the",
  exits: {
    north: "Sandy Beach",
  },
  contains: "salt water",
  // Because this room is an ocean, when the player
  // arrives here, we want to indicate that they're
  // floating in the ocean, and we want to treat the
  // ocean like it's a nest that the player is in.
  // We can force the player to be nested on arrival
  // by setting room.nest_player to true.
  // Now, when player arrives here, they will be
  // "(floating in the salt water)"
  nest_player: true,
  nests: { in_posture: "float", in_can_swim: true },
});

// --------------------------------------------------
// Rooms - Eastern Beach
// --------------------------------------------------

G.createAsset({
  class: "Room",
  name: "Eastern Beach",
  description: `Welcome to the Eastern Beach. The inland ocean extends to the far horizon. `,
  exits: {
    // south: "Inland Ocean",
    west: "Sandy Beach",
  },
  contains: "sand",
});

// TAKE WATER - SOLUTION v2
// As we wrote above, we've placed the player on a
// beach, where there's a reasonable expectation
// that they should be able to access sand or water.
// What's more, they should be able to enter the
// water and have it move them to the Inland Ocean.
// The room contains sand, and player can refer to
// the ocean by way of an exit. So, we're going
// to turn the exit itself into a reservoir.
G.createAsset({
  class: "Exit",
  // Exits usually aren't named, but we want to
  // name this exit after its destination.
  name: "Inland Ocean",
  direction: "south",
  place: { in: "Eastern Beach" },
  destination: "Inland Ocean",
  description: `
    The Inland Ocean laps against the shore, 
    and extends south to the far horizon. 
  `,
  // By setting contains, we're adding a vessel
  // to this exit and turning it into a reservoir.
  // It's abnormal, but not illegal.
  contains: "salt water",
});

// --------------------------------------------------
// Adding Exits
// --------------------------------------------------

// --------------------------------------------------
// Adding Doors
// --------------------------------------------------

// --------------------------------------------------
// Adding False Exits
// --------------------------------------------------

// --------------------------------------------------
// Adding Walls
// --------------------------------------------------

// --------------------------------------------------
// Adding Phenomena
// --------------------------------------------------

// --------------------------------------------------
// Adding Scenery
// --------------------------------------------------

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.js
└── css
|   └── adventurejs.css
└── games
    ├── demogame01
    ├── demogame02
    └── demogame03

 

Example Code

MyGame.createAsset({
  class: "Room",
  name: "Throne Room",
  description: `
    This cavernous, high vaulted room is empty but for 
    supporting pillars and a great central throne. 
  `,
});
  

Adding a Reservoir

A room can be a substance reservoir, meaning that it can contain an infinite supply of a specified substance. So, for instance, if your player enters a desert room, there will always be sand for them to interact with. (Although, note that substances aren't defined by default and that you'll have to define your own sand.)

MyGame.createAsset({
  class: "Room",
  name: "Endless Dune Sea",
  description: `
    This endless dune sea is full of sand. 
  `,
  contains: "sand",
});

MyGame.createAsset({
  class: "Solid",
  name: "sand",
  description: "It's sand. ",
});
  

Now, when a player tries something like dig in sand or fill bucket with sand, the parser will automatically direct the verb toward the room's reservoir.

A room can only contain one substance, so if you need two infinite substances, as you might find on a beach where there is both sand and water, you would need to place another reservoir in the room. In the case of our beach example, you might create an ocean object that is a water reservoir.

MyGame.createAsset({
  class: "Room",
  name: "Beach",
  description: `
    The sand runs down to the water. 
  `,
  contains: "sand",
});

MyGame.createAsset({
  class: "Ocean",
  name: "inland ocean",
  description: "The inland ocean meets the far horizon. ",
  contains: "water",
});
  
TMI What contains: "sand" does under the hood

Rooms are technically containers like any other asset. They have an in aspect with a substance vessel. The contains property is a shortcut method that sets room.aspects.in.vessel.substance_id to the specified substance, and sets its quantity to infinite, equivalent to this:

MyGame.createAsset({
  class: "Room",
  name: "Endless Dune Sea",
  description: `
    This endless dune sea is full of sand. 
  `,
  aspects: {
    in: {
      vessel: {
        substance_id: "sand",
        max_volume: Infinity,
        volume: Infinity,
      },
    },
  },
});
  

Adding Exits

As with many systems in AdventureJS, there is a shorthand method and a longhand method for adding exits.

Exit Shorthand

The simplest way to create an exit is just to define a property at room.exits.foo. If there is a room with the name that you set here, an exit will be created at game initialization. Exits are one-way. To make a two-way passage, you will need to add another exit at the other end.

MyGame.createAsset({
  class: "Room",
  name: "Start Room",
  description: `This is the Start Room. `,
  exits: {
    east: "East Room",
    west: "West Room",
    north: "North Room",
    south: "South Room",
  },
});

Exit Longhand

The longhand method is useful if you want to control specific properties of an exit: perhaps to give it a special description, or do something non-standard with it. Under the hood, the shorthand method results in an exit that looks like this.

MyGame.createAsset({
  class: "Room",
  name: "Sandy Beach",
  description: `This is the Sandy Beach. `,
  exits: {
    // south: "Inland Ocean", // we're not using this
  },
});

// We're using the longhand because we want 
// to do something unusual with this exit.
MyGame.createAsset({
  class: "Exit",
  // Exits usually aren't named, but we want to
  // name this exit after its destination.
  name: "Inland Ocean",
  direction: "south",
  place: { in: "Sandy Beach" },
  destination: "Inland Ocean",
  // We want to give a custom description to this exit.
  description: `
    The Inland Ocean laps against the shore, 
    and extends south to the far horizon. 
  `,
  // We're turning this exit into a substance 
  // reservoice via the contains property,
  // which will provide a response to "get water".
  // It's abnormal, but not illegal.
  contains: "salt water",
});

Adding Doors

Exits and doors are distinct things. An exit is just a connection from one room to another with no physical properties of its own. By default, if you create an exit going north, the player can just type "north" to go north. When you're ready to add complications like locks and keys, that's where a door comes in.

Technically, doors are instances of class Aperture. Apertures aren't just doors: an aperture can be a window, or a manhole cover, or a portcullis. In this example we're going to create a window.

MyGame.createAsset({
  class: "Exit",
  direction: "out",
  place: { in: "Start Room" },
  destination: "Playground",
  descriptions: {
    look: "You see a children's playground beyond the window. ",
    // exits can have descriptions that are printed when they are used
    travel: "You climb out the window and land in the playground. ",
  },
  // Here's how we specify an aperture
  aperture: "casement window",
});

MyGame.createAsset({
  class: "Window",
  name: "casement window",
  // We can set multiple descriptions for our aperture
  descriptions: {
    look: "It's a casement window. Beyond it is a playground. ",
    out: "Looking out the window reveals a playground. ",
    through: "Through the window you see the playground. ",
  },
  // We're going to set this window so that it is locked,
  // but can be unlocked without a key
  is: { closed: true, locked: true },
  dov: { lock: { with_nothing: true }, unlock: { with_nothing: true } },
  // Apertures only exist in one place, but we can create 
  // another asset in the destination room and sync them up. 
  other_side: "outside window",
});

Adding Walls

Scenery walls are a type of built-in scenery that are globally available. They are not turned on by default, but they can easily be enabled per room, per zone, or game-wide. Scenery walls are singular – meaning, there's only one east wall asset for the whole game – but they can have different descriptions per room or per zone. Scenery walls can be enabled through the area_scenery property.

MyGame.createAsset({
  class: "Room",
  name: "Walled Room",
  description: `
    Welcome to the Walled Room. This room uses 
    area_scenery to place walls.
    Look at the cardinal walls: 
    east wall, west wall, north wall, south wall. 
  `,
  exits: {
    west: "Start Room",
  },
  area_scenery: {
    "east wall": {
      enabled: true,
      description:
        "The east wall is dominated by a very large, ornate portrait. ",
    },
    "west wall": {
      enabled: true,
      description: "The west wall is broken by a passage west. ",
    },
    "north wall": {
      enabled: true,
      description: "The north wall is tiled in bright arabic patterns. ",
    },
    "south wall": {
      enabled: true,
      description: "The south wall hides a terrible secret. ",
    },
  },
});

Adding False Exits

False exits are a kind of scenery that represents an exit that isn't present in the current room. The point of them is to provide an informative response when a player tries to use an exit that isn't there. There are two kinds of false exits.

The first type uses the shorthand form of creating exits. Just set an exit to a string that is not the name of a room. If a player tries to use that exit, the string will be printed back to them.

MyGame.createAsset({
  class: "Room",
  name: "Start Room",
  description: `This is the Start Room. `,
  exits: {
    north: "North Room",
    south: "South Room",
    up: `
      You spread your wings and leap into the sky... 
      oh but wait, you haven't got any wings. You hop 
      a few inches off the ground, but don't get anywhere.
    `,
  },
});

The second method for setting false exits uses a type of built-in scenery that works the same way as walls and phenomena. A chief benefit of these is that they can be set for zones. False exits are globally available, and they are turned ON by default. Until they are customized, they will print a default "you can't go that way" sort of message. These are singular – meaning, there's only one false east exit asset for the whole game – but they can have different descriptions per room or per zone. This kind of false exits can be customized through the area_scenery property.

MyGame.createAsset({
  class: "Room",
  name: "Start Room",
  description: `This is the Start Room. `,
  exits: {
    up: "Attic",
    down: "Basement",
  },
  area_scenery: {
    east: {
      enabled: true,
      description: "The way east is blocked by tall mountains. ",
    },
    west: {
      enabled: true,
      description: "The way west is blocked by tall trees. ",
    },
    north: {
      enabled: true,
      description: "The way north is blocked by ice floes. ",
    },
    south: {
      enabled: true,
      description: "The way south is blocked by the sea. ",
    },
  },
});

False exits can also be used to print a list of true exits: if game.settings.print_exits_when_travel_fails is true (and it is true by default), your string will be followed by a list of actual exits.

MyGame.settings.set({
  print_exits_when_travel_fails: true,
});

Adding Phenomena

Just like walls, phenomena are a type of built-in scenery that are globally available to describe natural phenomena, like the sun, moon, and stars. They are not turned on by default, but they can easily be enabled per room, per zone, or game-wide. Phenomena are singular – meaning, there's only one sun asset for the whole game – but they can have different descriptions per room or per zone. Phenomena can be enabled through the area_scenery property.

MyGame.createAsset({
  class: "Room",
  name: "Mountain Forest",
  description: `
    Welcome to the Mountain Forest. A mountain slope rises above {us}. 
    The slope is covered with a mix of evergreen and conifer trees. 
  `,
  exits: {
    south: "Start Room",
  },
  area_scenery: {
    trees: {
      enabled: true,
      description:
        "A mixture of evergreen and conifer trees fill the mountain slope. ",
      synonyms: "evergreen, conifer",
    },
    mountain: {
      // for built-ins, it's necessary to specify enabled: true
      enabled: true,
      // This description only applies to this room.
      // We can use mountain elsewhere with different descriptions.
      description: "The mountain slope rises impassably above {us}. ",
      // We can add synonyms and adjectives to built-in scenery.
      synonyms: "slope",
    },
    // If there's one mountain, it's reasonable that player might 
    // use "mountains", so we'll include that too.
    mountains: {
      enabled: true,
      description: "The range of mountains extend to the ends of sight. ",
    },
  },
});

Adding Custom Scenery

There are two ways to create scenery. One way is by the same createAsset() function that we use for all assets. The benefit of using custom scenery assets is that they can be fully customized like any other asset.

MyGame.createAsset({
  class: "Room",
  name: "Mountain Forest",
  description: `
    Welcome to the Mountain Forest. Though it's still daylight, a few fireflies
    spark dimly in the shadows.
  `,
  exits: {
    south: "Start Room",
  },
});

MyGame.createAsset({
  class: "Scenery",
  name: "fireflies",
  description: `
    A rare few fireflies spark dimly in the shadows 
    beneath some of the older trees. 
  `,
  place: { in: "Mountain Forest" },
});

The other way is to use the same area_scenery property that we use for walls, phenomena, and false exits. Though area scenery are less configurable, the benefit of using them is that they can be set for zones or game-wide.

MyGame.createAsset({
  class: "Room",
  name: "Mountain Forest",
  description: `
    Welcome to the Mountain Forest. Tiny white aphids dance in rays of 
    light between the trees.
  `,
  exits: {
    south: "Start Room",
  },
  area_scenery: {
    aphids: {
      // enabled: true, // not necessary for one-offs
      adjectives: ["woolly", "ash", "tiny", "white", "smoky-winged"],
      synonyms: ["cloud", "clouds"],
      description: `
        Clouds of tiny, white, smoky-winged ash aphids 
        drift gently among the trees, occasionally catching rays 
        of afternoon sunlight. 
      `,
    },
  },
});

Further Reference