Pre-release
AdventureJS Docs Downloads
Score: 0 Moves: 0
Example game featuring use of the GUI system and the verbs open, close, lock, unlock. GUI, open, close, lock, unlock, key GUIOpenDemo, UI, SittingRoom, Library, Playground, Objects, Scorecard UI

Examples:GUI Open Demo

To be written...

 


// GUIOpenDemo.js
// ----------
/*global adventurejs A GUIOpenDemo*/

var GUIOpenDemo = new adventurejs.Game("GUIOpenDemo", "GUIOpenDemoDisplay").set(
  {
    // title, version, and author are shown in the title bar
    title: "GUI Open Demo",
    version: "0.0.1",
    author: "Ivan Cockrum",
    description: "This is my great game! Thanks for playing!",
  }
);

GUIOpenDemo.settings.set({
  // if this is true, lists of exits will
  // show the names of rooms they lead to
  show_room_names_in_exit_descriptions: true,

  // if this is true, lists of exits will
  // only include room names known to player
  show_room_names_in_exit_descriptions_only_when_room_is_known: false,

  // if this is true, lists of exits will only
  // show room names for exits player has used
  show_room_names_in_exit_descriptions_only_after_exit_has_been_used: false,

  // if this is true, verbose room descriptions will be
  // shown on first visit regardless of verbosity settings
  print_verbose_room_descriptions_on_first_visit: true,

  // if this is true, adventurejs will print
  // debug messages to the game display
  // debug_keywords: { general: true },

  // set this to set a default response to
  // player input that is not understood
  if_parser_has_no_response_print_this: "I have no response to your input. ",

  // set this to provide a default
  // response to blank input
  if_input_is_empty_print_this: "I didn't see any input. ",

  // alternately, game can be set to print
  // the current room description with
  // if_input_is_empty_print_room_description: true
});

// Foyer

GUIOpenDemo.createAsset({
  class: "Room",
  name: "Open Demo Foyer",
  image: "foyer",

  descriptions: {
    look: `Welcome to the Open Demo, where you can try opening and closing things, and locking and unlocking and picking locks, with the verbs open, close, lock, unlock, pick. Visit the other rooms in this demo to find some demonstrations. `,
    brief: `Try verbs. Open, close, lock, unlock, pick. `,
    smell: `{We} smell a bit of mildew from the outside damp, turning to the comforting smells of a warm home. `,
    sound: `{We} {don't} hear much. The wood panelling effectively muffles the outside, and there doesn't seem to be anyone moving inside the house. `,
  },

  exits: {
    // This is a shortcut for creating an Exit. It's useful
    // for exits that don't have any special properties or
    // an Aperture, which is an Exit's physical presence.
    east: "Sitting Room",
  },
}); // Open Demo Foyer

// Player

GUIOpenDemo.createAsset({
  class: "Player",
  name: "Tim Hunter",
  place: { in: "Open Demo Foyer" },
  is: { active: true },
  pronouns: "second",
});


// UI.js
// ----------
/*global adventurejs A GUIOpenDemo*/

// creating an image lookup makes these images available anywhere
// in the game.
GUIOpenDemo.createImageLookup({
  images: [
    {
      id: "playground",
      image: "/examples/GUIOpenDemo/images/background_playground01.jpg",
    },
    {
      id: "sittingroom",
      image: "/examples/GUIOpenDemo/images/background_sittingroom03.jpg",
    },
    {
      id: "library",
      image: "/examples/GUIOpenDemo/images/background_library03.jpg",
    },
    {
      id: "foyer",
      image: "/examples/GUIOpenDemo/images/background_foyer03.jpg",
    },

    {
      id: "brasskey",
      image: "/examples/GUIOpenDemo/images/icon_brass-key.png",
    },
    { id: "hairpin", image: "/examples/GUIOpenDemo/images/icon_hairpin.png" },
    { id: "ann", image: "/examples/GUIOpenDemo/images/icon_raggedy-ann.png" },
    {
      id: "andy",
      image: "/examples/GUIOpenDemo/images/icon_raggedy-andy-alt.png",
    },
    {
      id: "bluekey",
      image: "/examples/GUIOpenDemo/images/icon_blue-steel-key.png",
    },
    {
      id: "drawing",
      image: "/examples/GUIOpenDemo/images/icon_drawing-alt.png",
    },
    {
      id: "silverkey",
      image: "/examples/GUIOpenDemo/images/icon_silver-key.png",
    },

    {
      id: "lantern",
      image: "/examples/GUIOpenDemo/images/icon_brass-lantern.png",
    },
    { id: "knapsack", image: "/examples/GUIOpenDemo/images/icon_knapsack.png" },

    { id: "look", image: "/examples/GUIOpenDemo/images/icon_look.png" },
    { id: "listen", image: "/examples/GUIOpenDemo/images/icon_listen.png" },
    { id: "smell", image: "/examples/GUIOpenDemo/images/icon_smell.png" },
    { id: "save", image: "/examples/GUIOpenDemo/images/icon_save.png" },
    { id: "restore", image: "/examples/GUIOpenDemo/images/icon_restore.png" },
  ],
});

GUIOpenDemo.createCompass({
  id: "MyCompass",
  cssclasses: ["custom"],
  createnodes: true,
});

GUIOpenDemo.createVerbDock({
  id: "MyLeftDock",
  cssclasses: ["custom", "left"],
  verbs: [
    "look",
    "listen",
    "smell",
    { verb: "save", parse: true, cssclasses: ["admin"] },
    { verb: "restore", parse: true, cssclasses: ["admin"] },
  ],
});

// It's possible to create multiple verb docks.
// For example, you might find it useful to put admin
// verbs like save/restore into a separate dock.
// GUIOpenDemo.createVerbDock({
//   id: "MyRightDock",
//   cssclasses: ["custom","right"],
//   verbs: [
//     {verb:"save",parse:true,cssclasses:["admin"]},
//     {verb:"restore",parse:true,cssclasses:["admin"]}
//   ],
// });

GUIOpenDemo.createInventoryDock({
  id: "MyInventoryDock",
  cssclasses: ["custom"],

  // Inventory docks can be limited to specific asset classes
  // by passing class names in assetclasses.
  // assetclasses: [ "Key", "WritingImplement" ],

  // Inventory docks can be limited to specific states, like is.worn,
  // to create an inventory dock that only shows worn items.
  // is: [ "worn", ],

  // Inventory docks can be limited to items within other specified items,
  // like only showing the contents of a knapsack.
  // is_in: [ "knapsack" ],
});

// image docks are for showing room backgrounds.
// They show one image that is defined in the current room's
// room.image property. In the future this may be expanded
// to make something more flexible.
GUIOpenDemo.createImageDock({
  id: "MyImageDock",
  cssclasses: ["custom"],
  type: "Room",
});


// SittingRoom.js
// ----------
/*global adventurejs A GUIOpenDemo*/

GUIOpenDemo.createAsset({
  class: "Room",
  article: "the",
  name: "Sitting Room",
  image: "sittingroom",
  descriptions: {
    look: "The sitting room has many things on which to sit. None of them are objects though, because this is not a sitting demo. I suppose we should have called it an opening room. Shut up about it already! Instead, try opening and closing things: the playground window, the library door, the chest. Oh, did I mention that there's a window and a door? ",
    brief: "Don't sit. Window. Door. ",
    smell:
      "Notes of cardamom and chamomile and lapsang souchong have permeated the room's heavy rugs. It smells like a thousand teatimes. ",
    //sound: "There's not much to hear. The room's heavy persian rugs absorb any sound. Though for a second {we} think {we} hear a child playing. ",
  },
  exits: {
    west: "Open Demo Foyer",
  },
});

// Sitting Room Out

GUIOpenDemo.createAsset({
  class: "Exit",
  direction: "out",
  place: { in: "Sitting Room" },
  destination: "Playground",
  descriptions: {
    for_exits_list: `out the window to the 
    Playground`,
    travel: `{We} climb clumsily out the window and tumble 
    to the playground, where you land on your bottom on a 
    soft tuft of grass. `,
  },
  aperture: "inside window",
});

// Sitting Room Out - Playground Window

GUIOpenDemo.createAsset({
  class: "Window",
  name: "inside window",
  synonyms: ["playground window", "playground"],
  place: { in: "Sitting Room" },
  direction: "out",
  descriptions: {
    // Note that this code block uses a combination of Javascript
    // ${native template literals} and AdventureJS {custom templates}.
    // ${one} with a dollar sign is a Javascript template literal.
    // The {one} without a dollar sign is an AdventureJS custom
    // template. These can be used in combination, but there are
    // important differences to be aware of.
    //
    // AdventureJS custom templates are just strings, and they are
    // only evaluated right before they are printed to the display.
    //
    // By contrast, Javascript template literals are essentially
    // variables nested inside strings, and they are evaluated by
    // the browser immediately when it encounters them. Meaning,
    // if it's in a string that is stored as a property, it will be
    // evaluated when the game initializes. If you need a template
    // literal not be evaluated until it is called, it needs to be
    // wrapped in a function that returns it.
    //
    // See https://adventurejs.com/doc/Scripting_CustomTemplates.html
    // for more info.

    look: () => `
      Through the window, {we} can see a child's playground. 
      Currently the window is 
      {inside window [is] locked [or] unlocked} and 
      {inside window [is] open [or] closed}.
      ${
        !GUIOpenDemo.$("inside window").$didDo("unlock")
          ? `The window locks and unlocks by way of a simple  
        sash latch that doesn't require any key. `
          : ""
      } 
      ${
        GUIOpenDemo.$("inside window").$is("locked")
          ? "Try opening the window without first unlocking it. "
          : ""
      } 
      ${
        !GUIOpenDemo.$("inside window").$is("closed")
          ? `{We} might just climb through it. `
          : ""
      }
      `,

    // This commented block below is an alternate version.
    // It may appear like it should work the same way,
    // but because it has a template literal in a string property,
    // it would throw an error when the game is launched.

    // look: `The window is
    // {inside window [is] open [or] closed} and
    // {inside window [is] locked [or] unlocked}.
    // It locks by way of a simple sash latch that doesn't require any key.
    // ${GUIOpenDemo.$('inside window').$is('locked')?
    //   'Try entering "unlock window then open it".':''}
    //   Beyond the window, {we} can see a child's playground. `,

    // Here, we've set a description to use if a player
    // inputs "look through window".
    through: "You can see a small child's playground through the window. ",

    // And here, we're redirecting "look out window"
    // to "look through window" so we can capture both
    // possibilities without duplicating the code.
    out: function () {
      return this.descriptions.through;
    },
  },

  is: {
    closed: true,
    locked: true,
  },
  dov: {
    // window.dov.unlock.with_nothing
    // allows the window to be unlocked without use of a key.
    unlock: {
      with_nothing: true,
      on_first_success: function () {
        if (!this.is.closed) {
          // In some circumstances, when a user inputs 'open thing',
          // the verb open may redirect to 'unlock thing'. If that
          // happened here, we want to account for it by returning
          // our custom open.on_first_success function.
          return this.dov.open.on_first_success();
        } else {
          return "{We} should easily be able to open it now. ";
        }
      },
    },

    // lock and unlock params have to be set independently.
    // This is so that you could, for instance, set it so that
    // once the player unlocks it, it can't be locked again.
    lock: { with_nothing: true },

    open: {
      // on_first_success calls this function and
      // appends this string to the native verb output,
      // only the first time the player opens the window.
      on_first_success: function () {
        GUIOpenDemo.scorecard.completeEvent("open window");
        return "Now that it's open {we} just might climb out of it. ";
      },

      // on_first_failure only prints a message
      on_first_failure: `But it's just a simple sash 
      latch that doesn't need any key to unlock it. `,
    },
  },

  // This inside window is linked to the outside window.
  // Apertures, aka doors and windows, only exist in one room.
  // For a door to be in two rooms, we actually need two doors:
  // one side in one room, the other side in the other room.
  // We handle each side as a unique asset. In order to keep them
  // in sync we use the linked_asset property, so that if one is
  // set as open, so is the other one.
  linked_asset: "outside window",
});

// Sitting Room North

GUIOpenDemo.createAsset({
  class: "Exit",
  direction: "north",
  place: { in: "Sitting Room" },
  destination: "Library",
  descriptions: {
    //look: "It looks frosty that way. ",

    // for_exits_list is a special description for exits,
    // that lets authors customize how individual exits
    // are described when the player is presented with a
    // list of exits.
    for_exits_list:
      "through the door north to the Library",
  },
  aperture: "library door",
});

// Sitting Room North Door - library Door

GUIOpenDemo.createAsset({
  class: "Door",
  name: "library door",
  place: { in: "Sitting Room" },
  is: {
    closed: true,
    locked: true,
  },
  dov: {
    //close: true, // already set for all instances of Door class
    open: {
      on_first_success: function () {
        GUIOpenDemo.scorecard.completeEvent("open library door");
        return "Now you can enter the Library.  ";
      },
    },

    // Note here how we're setting it so the library door can be
    // unlocked with the brass key.
    unlock: {
      with_assets: ["brass key"],
      with_nothing: false,
    },

    // We set the assets for each verb, lock and unlock, explicitly
    // because it's common that an author might want a player to
    // unlock a door and then never have to interact with it again.
    // So if we want the key to also lock the door, we have to say so.
    lock: {
      with_assets: ["brass key"],
      with_nothing: false,
    },
  },
  linked_asset: "sitting room door",
  direction: "north",
  descriptions: {
    look: "A lovely Victorian nine panel oak door with frosted glass and a scuffed brass lock plate. ",
    through: function () {
      // Here we're setting a description for "look through door"
      // that returns a different value depending on whether the
      // door is open or closed.
      // "through" is what we call an Aspect, along with behind,
      // in, on, under, and many other prepositions. Any Aspect
      // can have a unique description set this way.
      if (!GUIOpenDemo.$("library door").$is("closed")) {
        return "Through the open door you can see shelves stuffed with books and books and books. ";
      } else {
        return "The glass panes in the door's upper half are quite heavily frosted, and don't reveal anything beyond. ";
      }
    },
  },
});

// coffee table

GUIOpenDemo.createAsset({
  class: "Table",
  name: "coffee table",
  place: { in: "Sitting Room" },
  description:
    "It's a low, rectangular coffee table, apparently hand carved from oak. ",
  adjectives: "wood, wooden, carved, oak",
  aspects: {
    on: {
      nest: { can: { enter: false } },
    },
  },
  dov: {
    go: {
      doBeforeTry: function (params) {
        // See how we're using a verb phase here. This is a way
        // to inject custom code into a verb when it is applied
        // to a particular asset. In this case by hooking into
        // go.doBeforeTry, we interrupt the verb before it can
        // run its default doTry conditional logic,
        // and print our own failure message. This basically means:
        // we don't care what the default logic has to say; you
        // just don't put your fat feet on the fine furniture.
        // See /doc/Scripting_VerbPhases.html for more info.
        GUIOpenDemo.print(
          "I mean, {we} could, but it would just be rude. ",
          "concatenate_output"
        );
        return false;
      },
    },
    sit: {
      // Here, we're setting sit, stand and lie to use
      // the same code as go.doBeforeTry. This is one way
      // you could apply the same custom logic to multiple
      // verbs on an object. There are also other ways to
      // achieve similar results, such as unsubscribing
      // the asset from these verbs, or using verb actions.
      doBeforeTry: function () {
        return this.dov.go.doBeforeTry();
      },
    },
    stand: {
      doBeforeTry: function () {
        return this.dov.go.doBeforeTry();
      },
    },
    lie: {
      doBeforeTry: function () {
        return this.dov.go.doBeforeTry();
      },
    },
  },

  // Here's an example of a verb reaction.
  // This will be superceded by the verb phases up above; to see
  // it in action you'll have to comment out the verb phases block.
  // Verb reactions are called when a particular action occurs,
  // such as moving the player onto an asset, regardless of what
  // verb caused it; go, or climb, or jump, or sit, or lie.
  // Every verb that results in moving the player into a thing
  // tries to call thing.doNestThatToThis(player).
  // Verb reactions may be common or unique. doNestThatToThis is a
  // very common reaction that will be called in numerous circumstances.
  // See /doc/Scripting_VerbActions.html for more info.
  doNestThatToThis: {
    // Nesting only applies to characters.
    // Tim Hunter is the name of our player character in this game.
    "Tim Hunter": function () {
      this.game.print(
        "Whoa there, podner! Let's keep those fat feet off the fine furniture. "
      );
      return false;
    },
  },

  // Alternately, if you wanted the same verb effect hook to apply to
  // all assets, you could define it as a function itself rather
  // than creating nested objects. This is also valid.
  // In this case, since the doNestThatToThis function is on the
  // coffee table, it would prevent all characters from nesting.

  // doNestThatToThis: function()
  // {
  //   this.game.print("Whoa there, podner! Let's keep those fat feet off the fine furniture. ");
  //   return false;
  // },
});


// Library.js
// ----------
/*global adventurejs A GUIOpenDemo*/

GUIOpenDemo.createAsset({
  class: "Room",
  name: "Library",
  image: "library",
  descriptions: {
    look: `The library is stuffed with books near 
    to the point of inducing claustrophia. But also cozy? 
    It's hard to say precisely. {We} scan the 
    overflowing shelves. Sadly, none of the books are objects. 
    Instead, take a look at the bureau and its drawers. `,

    brief: "Books. Desk. Door south. ",
    smell: "It's filled with the unique musty smell of old books. ",
    sound:
      "It is dead silent. You could hear a pin drop, if you pulled up the carpets first. ",
  },
}); // Library

// Sitting Room south Exit

GUIOpenDemo.createAsset({
  class: "Exit",
  direction: "south",
  place: { in: "Library" },
  destination: "Sitting Room",
  descriptions: {
    //look: "A heavy Victorian nine panel oak door with frosted panes. ",
    for_exits_list:
      "south to the Sitting Room",
  },
  aperture: "sitting room door",
});

// Sitting Room south door
// Exits have no physical properties. In order to apply
// physical properties to an exit, it needs an Aperture.
// Apertures can be manipulated like other tangible assets.

GUIOpenDemo.createAsset({
  class: "Door",
  name: "sitting room door",
  place: { in: "Library" },
  direction: "south",
  descriptions: {
    look: "It's a Victorian 9 panel oak door. ",
    open: "The sitting room door is open. ",
    closed: "The sitting room door is closed. ",

    // Assets can have a number of descriptions to use in
    // different contexts. Descriptions can be strings or
    // arrays or functions. This example shows a function
    // that uses native Javascript logic to return a string.
    // See /doc/Scripting_VerbSubscriptions.html for more info.
    through: function () {
      // This is one way to refer to objects in custom code:
      // GameName.$( "asset name" ).is("property")
      // See /doc/Scripting_CustomCode.html for more info.
      if (!GUIOpenDemo.$("sitting room door").$is("closed")) {
        return "Through the open door {we} can see the Sitting Room. ";
      } else {
        return "{We} can't see anything through the door's frosted panes. ";
      }
    },
  },
  linked_asset: "library door",
  is: {
    closed: true,
    locked: true,
  },
  dov: {
    open: true,
    close: true,
    unlock: { with_assets: ["brass key"] },
    lock: { with_assets: ["brass key"] },
  },
});

// bureau desk

GUIOpenDemo.createAsset({
  class: "Desk",
  name: "bureau desk",
  adjectives: ["oak", "wood", "wooden", "substantial"],
  synonyms: [],
  place: { in: "Library" },
  descriptions: {
    // Note that this description is an ordinary string property. Elsewhere in this demo
    // we're using traditional functions or arrow functions because they include
    // Javascript template literals. This example only includes AdventureJS custom
    // templates, which do not get evaluated until output, and are safe to use in strings.
    // See /doc/Scripting_CustomTemplates.html for more info.
    look: `
      It's a substantial oak bureau desk with multiple drawers stacked vertically, 
      top, middle and bottom. The top drawer is { top drawer [is] open [or] closed }, 
      the middle drawer is { middle drawer [is] open [or] closed }, and the bottom 
      drawer is { bottom drawer [is] open [or] closed }. `,
  },
  aspects: {
    on: {
      nest: { can: { enter: false } },
    },
  },
});

// top drawer

GUIOpenDemo.createAsset({
  class: "Drawer",
  name: "top drawer",
  adjectives: ["desk", "scratched"],
  synonyms: ["top drawer lock", "lock", "lock plate", "scratches"],

  // Note how we're using top_drawer.description here whereas we've used
  // desk.descriptions.look above. All assets can have multiple
  // descriptions. Setting asset.description is a shortcut to setting
  // asset.descriptions.look, for when an asset only needs one description.
  look: `The top drawer's copper lock plate 
    seems to be particularly scratched up. 
    Currently the drawer is { top drawer [is] open [or] closed }. `,

  place: { attached: "bureau desk" },
  is: {
    // Note how we're setting known:true here.
    // By default, players can't interact with
    // assets until they're known.
    // Also by default, attached assets don't
    // become known until a player examines
    // the asset they're attached to.
    known: true,

    closed: true,
    locked: true,
    listed: false,
  },
  dov: {
    // AdventureJS treats 'pick' and 'unlock' as distinct verbs.
    // If a player tries to use 'unlock' where 'pick' is defined,
    // the parser will try to redirect to 'pick'.

    pick: { with_assets: ["copper hairpin"] },
    // Pick and unlock are distinct verbs, to allow authors to
    // create precise situations. But, making them distinct can
    // lead to finicky situations that are unpleasant for players.
    // It's a fine line. We could either set it so that both verbs
    // work the same way:
    // unlock: { with_assets: ['copper hairpin'] },
    // Or we give the player a hint, if we don't want unlock to work.
    unlock: { on_failure: `{We} might be able to pick it with something. ` },

    open: {
      on_first_failure: "Bet {we} didn't see that coming, huh? ",
      on_first_success: function () {
        GUIOpenDemo.scorecard.completeEvent("open top drawer");
        //return "  ";
      },
    },
  },
});

// middle drawer

GUIOpenDemo.createAsset({
  class: "Drawer",
  name: "middle drawer",
  synonyms: "middle desk drawer",
  description: "The middle drawer is { middle drawer [is] open [or] closed }. ",
  adjectives: "desk",
  place: { attached: "bureau desk" },
  is: {
    known: true,
    closed: true,
    listed: false,
  },
});

// bottom drawer

GUIOpenDemo.createAsset({
  class: "Drawer",
  name: "bottom drawer",
  synonyms: "bottom desk drawer",
  description: "The bottom drawer is { bottom drawer [is] open [or] closed }. ",
  adjectives: "desk",
  place: { attached: "bureau desk" },
  is: {
    known: true,
    closed: true,
    listed: false,
  },
});

// drawers collection

// Collections are a special class that allows players to refer
// to a group of objects and try to apply a verb to all of them,
// or get back a combined description.
// See doc/adventurejs.Collection.html for more info.
GUIOpenDemo.createAsset({
  class: "Collection",
  name: "desk drawers",
  place: { attached: "bureau desk" },
  collection: ["top drawer", "middle drawer", "bottom drawer"],
  synonyms: ["drawers", "three drawers"],
  is: {
    known: true,
    listed: false,
  },
  descriptions: {
    look: function () {
      var openCount = [];
      var closedCount = [];
      var msg = "";

      msg += `The bureau has three drawers stacked vertically: 
      top, middle and bottom. The top drawer has a delicate copper 
      lock plate that appears to be rather scratched up. 
      Currently, `;

      // Note how we're using 'this' here. Under normal circumstances
      // nested properties don't have access to their top level object.
      // AdventureJS uses asset.descriptions.look.call(asset)
      // to ensure that asset descriptions can refer to their asset.
      // Any property that relies on getStringOrArrayOrFunction()
      // will have 'this' scoped to its parent asset.
      // See /doc/Scripting_StringArrayFunction.html for more info.
      for (var i in this.collection) {
        // this.collection contains asset names.
        // We want to get references to the asset objects.
        // One way to do this is with GameName.$("my asset")
        // See /doc/Scripting_CustomCode.html for more info.
        let drawer = GUIOpenDemo.$(this.collection[i]);

        // Once we have an asset reference, asset.$is(property)
        // is a safe way to try to get asset properties.
        drawer.$is("closed")
          ? closedCount.push(drawer.name)
          : openCount.push(drawer.name);
      }

      if (0 === openCount.length) {
        msg += "all three drawers are closed.";
      } else if (0 === closedCount.length) {
        msg += "all three drawers are open.";
      } else if (2 == openCount.length) {
        msg += `the ${openCount[0]} and ${openCount[1]} 
        are open, while the ${closedCount[0]} is closed. `;
      } else if (2 == closedCount.length) {
        msg += `the ${closedCount[0]} and ${closedCount[1]} 
        are open, while the ${openCount[0]} is open. `;
      }

      return msg;
    },
  },
});


// Playground.js
// ----------
/*global adventurejs A GUIOpenDemo*/

GUIOpenDemo.createAsset({
  class: "Room",
  article: "the",
  name: "Playground",
  image: "playground",
  descriptions: {
    look: `Fireflies speckle the dusk with softly pulsing 
    motes of light. {We've} come to a modest child's playground 
    around back of the house. The playground has many things with 
    which to play, but none of them are objects, and we're not 
    going to get into it, ok? A window leads back into the house. `,

    brief: `Play here. Go window. `,

    smell: `A recent rain has stirred up odors of mouldy leaves and fresh new growth. `,
    sound: `A light breeze ruffles the treetops. {We} can hear it passing from here to there, rattling the trees in its wake. Crickets chirp quietly from the shadows beneath the trees. `,
  },
  room_scenery: {
    fireflies: {
      enabled: true,
      description:
        "Fireflies pulse gently here and there, lending a charming quality to the darkening playground. ",
    },
    grass: {
      enabled: true,
      description:
        "Occasional tufts of glossy medium height grass dot the yard. ",
    },
  },
});

// Playground in

GUIOpenDemo.createAsset({
  class: "Exit",
  direction: "in",
  place: { in: "Playground" },
  destination: "Sitting Room",
  descriptions: {
    for_exits_list: `in to the Sitting Room`,
    travel: `{We} grab hold of the window sill, scrabble awkwardly up 
    the siding, and heft {ourself} over the sill and in through the window, 
    where {we} tumble into the sitting room. `,
  },
  aperture: "outside window",
});

// outside window

GUIOpenDemo.createAsset({
  class: "Window",
  name: "outside window",
  synonyms: ["house"],
  place: { in: "Playground" },
  direction: "in",
  descriptions: {
    // See how this example uses a traditional function.
    // Traditional functions are able to be scoped to their
    // top level assets which means you can use 'this' with them.
    // By contrast if you look at the code for the inside
    // window description, you'll see that it uses an arrow
    // function. Arrow functions are permitted, but because
    // they can't be scoped to their parent asset, they
    // can't use 'this'.
    look: function () {
      return `
      Though the sitting room seemed dimly lit, as it is 
      now dusk outside, the light through the window seems 
      quite bright from out here.
      The window is {inside window [is] open [or] closed}.
      ${
        !this.is.closed
          ? "{We} should be able to climb back inside. "
          : "{We'll} need to open the window if {we} hope to get back through it. "
      }
      `;
    },

    // We could instead have used a traditional function here,
    // and those do receive scope, which would have let us use
    // 'this' to refer to the window.

    through:
      "The wan light of the sitting room seems quite bright from out here. ",
    in: function () {
      return this.descriptions.through;
    },
  },
  is: {
    closed: true,
    locked: true,
  },
  dov: {
    lock: { with_nothing: true },
    unlock: { with_nothing: true },
  },

  // Apertures, aka doors and windows etc, may exist in two places
  // one side in one room, and another side in another room.
  // We handle each side as a unique asset. In order to keep them
  // in sync, meaning open/closed and locked/unlocked, we use the
  // linked_asset property. This outside window asset is linked to
  // its other side, a corresponding inside window asset.
  linked_asset: "inside window",
});


// Objects.js
// ----------
/*global adventurejs A GUIOpenDemo*/

GUIOpenDemo.createAsset({
  class: "Chest",
  name: "small antique chest",
  indefinite_article: "a",
  place: { on: "coffee table" },

  descriptions: {
    look: function () {
      let msg = `It's a small antique Victorian chest, 
      lacquered in a glossy prussian blue and bound with 
      ornate blue steel straps that are pitted with age. 
      Its lid is 
      {small antique chest [is] locked [or] unlocked} and 
      {small antique chest [is] open [or] closed}. `;
      if (this.game.$("small antique chest").$is("open")) {
        msg += `The chest is lined with a rich egg yolk 
          yellow brocade that presents a startling contrast 
          to its dark exterior. `;
      }
      return msg;
    },
  },
  adjectives: ["victorian", "prussian", "blue", "steel", "glossy", "old"],
  synonyms: ["straps"],
  is: { locked: true, closed: true },
  dov: {
    open: {
      with_nothing: true,
      on_first_failure: `{We} didn't think it was going to be that easy, did {we}? `,
      on_first_success: function () {
        GUIOpenDemo.scorecard.completeEvent("open chest");
        //return "  ";
      },
    },
    unlock: { with_assets: ["blue steel key"] },
    lock: { with_assets: ["blue steel key"] },
  },
});

GUIOpenDemo.createAsset({
  class: "Paper",
  name: "goldenrod sheet",
  image: "drawing",
  description: `It's a sheet of goldenrod colored paper with 
    a child's drawing on it. The drawing appears to feature  
    a girl with fire engine red hair. The name "Copper" is 
    scrawled above her in big childlike letters. `,
  place: { in: "top drawer" },
});

GUIOpenDemo.createAsset({
  class: "Key",
  name: "brass key",
  image: "brasskey",
  synonyms: ["glass bead"],
  article: "a",
  adjectives: ["burnished", "chonky"],
  place: { on: "coffee table" },
  description: `It's a burnished brass key with chonky teeth 
  and a small glass bead mounted in the head. `,
  iov: {
    unlock: {
      // AdventureJS offers many ways to override its default
      // actions and output. If
      // asset.dov.unlock.on_failure
      // is a string, it will be appended to the default unlock
      // failure message. In this case, we want to completely
      // override the output, so we call this.game.overrideTurn()
      // this.game is the same as GUIOpenDemo when called from a scoped function.
      // on_failure: function(){ this.game.overrideTurn(
      //   `Whoops! This brass key doesn't unlock that ${GUIOpenDemo.getInput().getAsset(1).name}. `
      // )},
      on_first_failure: function () {
        this.game.overrideTurn(
          `Whoops! Looks like {we} can't unlock ${
            GUIOpenDemo.getInput().getAsset(1).articlename
          } with the brass key. `
        );
      },

      on_success: `Now you can open the door. `,
      //then_destroy: `Oh no! The brass key broke! `,

      // There's something missing here. We haven't set this key to
      // unlock: { with_assets: ['sitting room door'] },
      // to indicate that this key can open that lock.
      // That's because we've already set that lock to
      // unlock: { with_assets: ['brass key'] }
      // When assets are linked through verb subscriptions,
      // you only have to set one or the other. Two-way connections
      // will be completed during game initialization.
    },
  },
  dov: {
    take: { on_first_success: "Now to find something to unlock with it. " },
  },
}); // brass Key

GUIOpenDemo.createAsset({
  class: "Key",
  name: "blue steel key",
  image: "bluekey",
  synonyms: ["blue key", "steel key"],
  adjectives: ["victorian"],
  iov: { unlock: { with_assets: ["small antique chest"] } },
  description: "It's an ornate Victorian key made of blue steel. ",
  // article: "a",
  place: { in: "top drawer" },
});

// GUIOpenDemo.createAsset({
//   class: "Key",
//   name: "tiny brass key",
//   description: "It's a tiny key, made of brass. ",
//   article: "a",
//   adjectives: ["tiny", "brass"],
//   // place: { in: "Sitting Room" },
//   iov: { 'unlock': {then_destroy: { with_result: "foo" }, } },
// });

// GUIOpenDemo.createAsset({
//   class: "Key",
//   name: "giant brass key",
//   description: "It's a giant novelty key that looks like it's made of brass. ",
//   article: "a",
//   adjectives: "giant, brass",
//   // place: { in: "Sitting Room" },
// });

GUIOpenDemo.createAsset({
  class: "Key",
  name: "tiny silver key",
  adjectives: ["tiny"],
  image: "silverkey",
  description:
    "It's tiny, and it's silver, and it's a key. It don't open nuthin' so don't even bother. ",
  article: "a",
  place: { in: "Open Demo Foyer" },
  //iov: { 'unlock': {then_destroy: { with_result: "foo" }, }, },
});

GUIOpenDemo.createAsset({
  class: "Doll",
  name: "Raggedy Ann doll",
  synonyms: ["dress", "trim", "hair"],
  image: "ann",
  place: { in: "small antique chest" },
  descriptions: {
    look: function () {
      return `It's a well-loved Raggedy Ann doll in a white dress 
      with blue and red trim. 
      ${
        GUIOpenDemo.$("copper hairpin").$is("attached", this)
          ? "A patch of her red hair is gathered into a loose pigtail by a hairpin. "
          : "Her messy red hair is by turns tangled and patchy. She looks overdue for some hair care. "
      } `;
    },
  },
  aspects: {
    attached: { list_contents_in_room: false, list_contents_in_examine: false },
  },
  dov: {
    attach: { with_nothing: true },
    take: {
      on_first_success: function () {
        GUIOpenDemo.scorecard.completeEvent("take ann");
        //return " ";
      },
    },
  },
});

GUIOpenDemo.createAsset({
  class: "Doll",
  name: "Raggedy Andy doll",
  synonyms: ["hat"],
  image: "andy",
  place: { in: "Playground" },
  aspects: {
    attached: { list_contents_in_room: false, list_contents_in_examine: false },
  },
  descriptions: {
    look: function () {
      return `It's a threadworn Raggedy Andy doll in a patched 
      sailor suit. His red hair is partially covered by a cap 
      that appears to be sewn on, though the old threads are wearing. 
      ${
        GUIOpenDemo.$("copper hairpin").$is("attached", this)
          ? "A copper hairpin is helping to hold his hat to his head. "
          : "Without the pin, his hat flops about loosely. "
      } `;
    },
  },
  dov: {
    attach: { with_nothing: true },
    take: {
      on_first_success: function () {
        GUIOpenDemo.scorecard.completeEvent("take andy");
      },
    },
  },
});

GUIOpenDemo.createAsset({
  class: "Lockpick",
  name: "copper hairpin",
  image: "hairpin",
  synonyms: ["pin", "beads", "tines", "head"],
  adjectives: ["hammered"],
  article: "the",
  dov: {
    attach: {
      with_nothing: true,
      on_success: function () {
        let indirect_object = this.game.getInput().getAsset(2);
        if (indirect_object.name === "Raggedy Ann doll") {
          GUIOpenDemo.scorecard.completeEvent("pin ann");
          return `The hairpin shapes Ann's ragged hair into a neatly groomed pigtail. `;
        }
        return `Attached hairpin. `;
      },
    },
    take: {
      on_first_success:
        "Good catch. It looks like it might be used to pick a lock. ",
      on_success: "{We} twirl it between {our} nimble fingers. ",
    },
  },
  iov: {
    pick: {
      with_assets: ["top drawer"],
      //then_destroy: "The hairpin snaps into pieces! {We'll} have to find Raggedy Ann a new one. ",
      on_failure:
        "The copper is quite soft and can't handle a full sized lock. ",
    },
  },
  descriptions: {
    throw: "It skitters across the floor. ",
    description: `The hammered copper hairpin has two tines and  
    a butterfly shaped head with tiny beads affixed to it, in  
    a vaguely art nouveau style. It's actually quite nice. `,
  },
  place: { attached: "raggedy andy doll" },
});

GUIOpenDemo.createAsset({
  class: "Knapsack",
  name: "knapsack",
  image: "knapsack",
  place: { in: "Tim Hunter" },
  description:
    "It's {our} trusty old canvas knapsack, worn to the shape of {our} back. It's been on many adventures with {us}. ",
  adjectives: ["worn"],
  is: { worn: true, known: true, seen: true },
});

GUIOpenDemo.createAsset({
  class: "Lantern",
  name: "brass lantern",
  image: "lantern",
  place: { in: "knapsack" },
  is: { known: true, seen: true },
  description:
    "It's {our} trusty old brass lantern. An honest to goodness antique! {We} never leave home without it. ",
  adjectives: [],
});


// Scorecard.js
// ----------
/*global adventurejs A GUIOpenDemo*/

GUIOpenDemo.scorecard.set({
  // This is how you set score events for your game.
  // You can add as few or as many as you like,
  // and set points to whatever number you like.
  // The names are up to you, so set them however you like.
  score_events: {
    "open window": 1,
    "take ann": 1,
    "open library door": 1,
    "open top drawer": 1,
    "open chest": 1,
    "take andy": 1,
    "pin ann": 1,

    // Let's say you want the game to start with
    // some points already set. You can do that like this.
    "preset points": { points: 5, complete: true, recorded: true },

    // You can set negative points too. These unset points cancel
    // out the preset points above.
    "unset points": { points: -5, complete: true, recorded: true },
  },

  // To attach a score event to your custom function, use
  // GUIOpenDemo.scorecard.completeEvent('open window');
  // You'll see these sprinkled throughout this demo code.

  // AdventureJS has built-in scoring functions,
  // but you may, if you like, write custom score handling.

  // this returns a string
  // score_message: `{Our} score went up! `,

  // this returns a custom function
  // score_message: function(){
  //   return `Dude, you totally just got ${this.diff} points!`
  // },

  // Or maybe you just want to tweak the score display.
  // By default score appears in 0/0 format, but let's
  // say you'd like it to say "Score: 0 out of 0".
  // score_format: function()
  // {
  //   return `Score: ${this.score} out of ${this.total}`;
  // }

  // score_message and score_format have access to some vars:
  // - this.score (old score)
  // - this.newscore (new score)
  // - this.diff (difference between old/new )
  // - this.total (total of points available)
});

Source files