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

Working With...Aspects

Aspect is the term we use for the parts of an asset that holds other assets, whether that's a surfaces or a container. Our primary five* aspects are: in, on, under, behind, attached. Any tangible asset can have aspects. For example, a pot might have an in aspect, which contains water. A table might have an on aspect, which holds papers. All rooms have an in aspect, which holds everything in the room, including the player. Assets can also have multiple aspects. For example, a bed might have an on aspect and also an under aspect. In short, aspects are how we put things in things.

* Aspects aren't limited to the core five. For example you might create an over aspect to provide a response to player input like look over wall.

 

Demo Game

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

var G = (WorkingWithAspects = new AdventureJS.Game(
  "WorkingWithAspects",
  "WorkingWithAspectsContainer"
));

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

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

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

// --------------------------------------------------
// Player (and their pen)
// --------------------------------------------------

G.createAsset({
  class: "Player",
  name: "Explorer",
  place: { in: "Aspects Laboratory" },
});

G.createAsset({
  class: "Pen",
  name: "pen",
  place: { in: "Explorer" },
  description: `
    Just an ordinary, everyday fountain pen. My pen. My pen!
  `,
});

// --------------------------------------------------
// Rooms
// --------------------------------------------------

G.createAsset({
  class: "Room",
  name: "Aspects Laboratory",
  description: function () {
    let msg = `
      Welcome to the Aspects Demo. Each of the objects here 
      features various types of aspects. A control studded 
      {a:console} stands near one wall of the room. 
      Against the wall opposite is a beaten {a:chesterfield sofa}. 
      At the very center of the room lies a {a:round platform}. 
      Floating over the platform is a {a:featureless gray cube}, 
      which appears to be slowly rotating on one axis. ${items()} 
    `;
    return msg;
  },
});

// --------------------------------------------------
// Console
// --------------------------------------------------

G.createAsset({
  class: "Desk",
  name: "console",
  synonyms: ["control", "control"],
  adjectives: ["studded"],
  place: { in: "Aspects Laboratory" },
  is: { listed: false },
  description: `
    The console has various controls and blinking lights 
    that we're not going to get into in this demo.
    More importantly, the console's surface features 
    a bisected platform that looks a bit like a dual-deck
    turntable, which looks like it can hold two items. 
    Below the console is a cabinet, currently 
    {cabinet[is]open[or]closed}.
  `,
  // The Desk class comes with an "on" aspect
  // predefined, but we want to limit the number
  // of assets that can be placed on the console.
  aspects: {
    on: {
      capacity: { count: 2 },
    },
    // We're going to attach the cabinet to the console,
    // and setting list_contents_in_examine to false
    // prevents the cabinet from being listed in the
    // console's description.
    attached: {
      list_contents_in_examine: false,
    },
  },
});

// The Cabinet class has an "in" aspect which isn't
// limited to any particular assets or classes,
// so a player can put all the strangelets in here.
G.createAsset({
  class: "Cabinet",
  name: "cabinet",
  adjective: "console",
  place: { attached: "console" },

  // is_closed: true is a shortcut to is: { closed: true }
  // any is.property can be set this way
  is_closed: true,
});

// --------------------------------------------------
// Chesterfield sofa
// --------------------------------------------------

G.createAsset({
  class: "Couch",
  name: "chesterfield sofa",
  place: { in: "Aspects Laboratory" },
  description: `
    One wall of the lab is dominated by an oxblood red 
    leather chesterfield sofa. Its many missing buttons  
    attest to its frequent use as an improvised bed.
  `,
});

G.createAsset({
  class: "Paper",
  name: "crumpled sheet of paper",
  synonyms: ["tesseract", "scratches", "scrawls", "sheet", "drawing"],
  description: `
    The crumpled sheet of paper bears various 
    scratches and scrawls. Chief among them is what 
    appears to be a crude drawing of a tesseract. 
  `,
  place: { under: "chesterfield sofa" },
});

// --------------------------------------------------
// Round platform
// --------------------------------------------------

G.createAsset({
  class: "Entity",
  name: "round platform",
  place: { in: "Aspects Laboratory" },
  description: function () {
    let msg = `
      The center of the room is filled by a broad round 
      platform dressed up with blinky sci-fi lights like 
      a right proper Star Trek prop. A featureless gray 
      cube floats over the platform. ${items()}
    `;
    return msg;
  },
  aspects: ["over"],

  // Prevent the platform being listed as inventory
  // because we're mentioning it in the room description.
  is: { listed: false },
});

// --------------------------------------------------
// Featureless cube
// --------------------------------------------------

G.createAsset({
  class: "Entity",
  name: "featureless cube",
  adjectives: ["gray", "grey"],
  description: function () {
    let msg = `
      There is a featureless gray cube here, floating above the floor 
      and slowly rotating on one axis. The cube appears to be solid, 
      and yet, through some strange trick of perspective, it also appears 
      to be hollow, as you can see the inside of it. ${items()}
    `;
    return msg;
  },

  // We're going to limit each aspect of this cube
  // to only permit one specific asset.
  aspects: {
    in: { accepts_assets: ["plumbus"] },
    on: { accepts_assets: ["voorheer"] },
    under: { accepts_assets: ["beznat"] },
    behind: { accepts_assets: ["armunk"] },
    attached: { accepts_assets: ["chipler"] },
    over: { accepts_assets: ["schmigibo"] },
  },
  place: { over: "round platform" },

  // We're using Entity class for this object,
  // which doesn't support "put in" and "take from"
  // so we need to add those.
  iov: ["put", "take"],

  is: { listed: false },
});

// We want to use a bit of dynamic text to describe
// the items in proximity to the cube. We want to do
// it in a couple of places, so we've made a small
// custom function to handle it.
const items = function () {
  let msg = "";
  const l = G.$("featureless cube").getAllNestedContents().length;
  switch (l) {
    case 0:
      // No text
      break;
    case 1:
      msg += `An item is positioned near the cube, floating relative to it. `;
      break;
    default:
      // Anything more than 1
      msg += `Some items are positioned near the cube, floating relative to it. `;
      break;
  }
  return msg;
};

// --------------------------------------------------
// Custom class
// --------------------------------------------------

// We're going to create a custom class
// for the items in the cube
G.createClass({
  extend: "Entity",
  class: "Strangelet",

  // We're going to add some verb subscriptions.
  // Our Strangelet class extends Entity, which
  // only has a few basic verbs. Strangelets will
  // need at least put and take.

  // This is a shorthand for adding verb subscriptions
  // to a custom class.
  //
  dov: ["put", "take"],

  // This also works.
  //
  // dov: {
  //   put: true,
  //   take: true,
  // },

  // If you want to customize the verb subscription,
  // use the longhand form.
  //
  // dov: {
  //   put: { on_failure: `You totally failed to put the thing. ` },
  //   take: { on_first_success: `You took the thing! ` },
  // },
});

// --------------------------------------------------
// Strangelets
// --------------------------------------------------

G.createAsset({
  class: "Strangelet",
  name: "plumbus",
  place: { in: "featureless cube" },
  description: `
    Looking at the plumbus, somehow {we} just know 
    that it can only fit <em>in</em> the featureless cube.
  `,
});

G.createAsset({
  class: "Strangelet",
  name: "voorheer",
  place: { on: "featureless cube" },
  description: `
    Somehow the voorheer communicates to {us} that 
    it is only meant to go <em>on</em> the featureless cube.
  `,
});

G.createAsset({
  class: "Strangelet",
  name: "armunk",
  place: { behind: "featureless cube" },
  description: `
    Ah, the armunk. The armunk, a little voice in {our} tells us,
    can only be placed <em>behind</em> the featureless cube.
  `,
});

G.createAsset({
  class: "Strangelet",
  name: "beznat",
  place: { under: "featureless cube" },
  description: `
    {Our} dear old grandmother had a beznat once. {We} remember
    that it could only ever be placed <em>under</em> the featureless cube.
  `,
});

G.createAsset({
  class: "Strangelet",
  name: "chipler",
  place: { attached: "featureless cube" },
  description: `
    Chipler, {our} dear chipler. It must only be <em>attached</em> 
    to the featureless cube.
  `,
});

G.createAsset({
  class: "Strangelet",
  name: "schmigibo",
  place: { over: "featureless cube" },
  description: `
    Why did {we} even get this schmigibo, anyway? 
    All it does is hover <em>over</em> the featureless cube.
  `,
});

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

 

Aspect Shorthand

Before we get into the details, know that there is a very brief shorthand method for adding aspects to assets. (Keep in mind that many asset classes have aspects predefined.)

MyGame.createAsset({
    class: "Chest",
    name: "treasure chest",
    place: "Treasure Room",
    aspects: [ "in", "on" ],
});

That one line, aspects: [ "in", "on" ], will add in and on aspects to an asset, with all of an aspect's default properties (we'll get into how to configure those in the next sections).

Basic Aspect Longhand

Using the longhand method, here's the very minimum needed to define an aspect. Most built-in asset classes already have aspects defined, but you never know when you'll want to create your own.

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { in: true },
});

By setting aspects: { in: true }, we're creating an in aspect that we can put other assets in. Here's how to put another asset in an aspect.

MyGame.createAsset({
  class: "Gem",
  name: "diamond",
  place: {in: "treasure chest"},
});

The form of place: {in: "treasure chest"} is how we store an asset's place under the hood, but there are a couple of alternative shorthands that you may use if you prefer.

This version uses place: "treasure chest". Since this shorthand does not specify an aspect, AdventureJS will use the chest's default aspect. Most built-in classes already have default aspects. If you're making your own custom classes and using this shorthand, just be sure that your classes have a default_aspect property.

MyGame.createAsset({
  class: "Gem",
  name: "diamond",
  place: "treasure chest",
});

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  default_aspect: "in",
});  

This version uses in: "treasure chest". Most built-in classes already have aspects where you'd expect them to be. If you try to place an asset in an aspect that doesn't exist on its target, one will be created during game startup.

MyGame.createAsset({
  class: "Gem",
  name: "ruby",
  // this only works for the five primary aspects: in, on, under, behind, attached
  in: "treasure chest",
});  

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  aspects: [in],
});  

Aspect Settings

Next we'll get into the granular details of controlling an aspect's behavior. In the example above where we set aspects: { in: true }, we're creating an in aspect with default settings. An aspect's settings allow you to control these properties:

accepts_assets

Specify only particular assets to be allowed in this aspect. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      accepts_assets: [ "ruby", "diamond", "emerald" ],
    } 
  },
});

accepts_classes

Specify only certain classes to be allowed in this aspect. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      accepts_classes: [ "Gem", "Coin" ],
    } 
  },
});

capacity

Specify capacity limits for assets placed in this aspect, including height, width, depth, quantity, and weight. For each property, -1 means infinite, aka no limits. These limits control what the player can put into the aspect, but not what the author can put in. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      capacity: { 
        height: 2,
        width: 4,
        depth: 3,
        count: 20,
        weight: 100,
       },
    } 
  },
});

excludes_assets

Specify particular assets that are not allowed in this aspect, so for instance accept class Gem, but not certain gems. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      excludes_assets: [ "coal", "stone" ],
    } 
  },
});

excludes_classes

Specify certain classes to exclude from this aspect, for instance to say we don't want anything Edible to be placed in this chest. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      excludes_classes: [ "Edible" ],
    } 
  },
});

know_contents_with_parent

Whether the contents of the aspect become known when the player knows the asset. By default, when an asset becomes known to a player, its contents also become known (unless inside a closed container). You might not want nested assets known until the player examines the asset. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      know_contents_with_parent: false,
    } 
  },
});

list_contents_in_examine

Whether the contents of the aspect are listed when a player examines the asset. By default, examining an asset also list its contents, but there may be cases where you don't want that behavior, such as when nested assets are attached to the main asset and already mentioned in the asset description, like this desk/bench combo. [More info...]

MyGame.createAsset({
  class: "Desk",
  name: "student desk",
  place: "Classroom",
  description: "It's an old-fashioned lift top school desk with an attached bench."
  aspects: { 
    attached: {
      list_contents_in_examine: false,
    } 
  },
});
MyGame.createAsset({
  class: "Chair",
  name: "bench",
  place: { attached: "student desk" },
});

list_contents_in_room

Whether the contents of the aspect are listed in the room description. This is useful for things that are baked into the room description. For example, imagine a typewriter on a desk that is included in the room description, so that you don't want it listed among the room's inventory. [More info...]

MyGame.createAsset({
  class: "Desk",
  name: "office desk",
  place: "Accounts Office",
  description: "The desk is dominated by an old Corona typewriter. "
  aspects: { 
    on: {
      list_contents_in_room: false,
    } 
  },
});
MyGame.createAsset({
  class: "Typewriter",
  name: "Corona typewriter",
  on: "office desk",
});

nest

Whether the player can enter the aspect. Players can't automatically enter aspects. Add a nest to allow a player to enter an aspect. For instance, a bathtub might need a nest so a player can enter it, but a bathroom sink probably would not. Nests have their own set of properties for managing how players can interact with them. [More info...], also see Working with Nests

MyGame.createAsset({
  class: "BasicBathtub",
  name: "obsidian tub",
  place: "Bathroom",
  aspects: { 
    in: {
      aspects: { in: { nest: { can: { enter: true } } } }
    } 
  },
});

orientation

Whether the aspect is horizontal or vertical. Aspect orientation is horizontal by default, but can be set to vertical. Currently this has no impact on aspect contents, but it may in the future. [More info...]

MyGame.createAsset({
  class: "Tree",
  name: "giant redwood",
  place: "Muir Woods",
  aspects: { 
    on: {
      orientation: "vertical",
    } 
  },
});

player_can_add_contents

Whether the player can add assets to the aspect. By default, players can add objects to aspects, unless limited by other options, but that can be disabled with this option. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      player_can_add_contents: false,
    } 
  },
});

player_can_remove_contents

Whether the player can remove assets from the aspect. By default, players can remove objects from aspects, unless limited by other options, but that can be disabled with this option. This is useful for attached assets, such as the drawers of a desk. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      player_can_remove_contents: false,
    } 
  },
});

see_contents_with_parent

Whether the contents of the aspect become seen when the player sees the asset. By default, when an asset is seen by a player, its contents also become seen (unless inside a closed container). You might not want nested assets seen until the player examines the asset. [More info...]

MyGame.createAsset({
  class: "Chest",
  name: "treasure chest",
  place: "Treasure Room",
  aspects: { 
    in: {
      see_contents_with_parent: false,
    } 
  },
});

vessel

Whether the aspect can contain substances. Aspects don't automatically handle substances. They need a Vessel attached to them. Vessels have their own set of properties for managing contents and quantities. [More info...]

MyGame.createAsset({
  class: "DrinkingGlass",
  name: "water glass",
  place: "Bathroom",
  aspects: { 
    in: {
      vessel: { volume: 250, substance_id: "water" },
    } 
  },
});

If an author tries to create an aspect on a class that already has one, the existing aspect will be used, but any settings set by the author will be applied to it.

Further Reference