Working With...Collections
A collection is like a piece of scenery. It's a special kind of asset that
only exists to point to a group of other assets. For instance, imagine a
desk with drawers, where the drawers are unique assets. We can create a
drawers collection to let the player input something like
examine drawers without having the game ask them
which drawer did you mean? Collections are intended primarily for
fixed items that are always in the same location, like our desk drawers, or
a group of tapestries in a throne room, or a set of unique gems on an idol.
They're not global, and they're not intended for assets that can move around
or be hidden or lost, like a set of juggling balls (though, with a stretch
and some custom code, they can be made to work that way).
Demo Game
// WorkingWithCollections.js
/* global AdventureJS A Game */
var WorkingWithCollections = new AdventureJS.Game(
"WorkingWithCollections",
"WorkingWithCollectionsContainer"
);
WorkingWithCollections.settings.set({
display: "inline",
layout: "classic",
title: "Working With Collections",
author: "Ivan Cockrum",
description: "Today we are working with collections. ",
version: "0.0.1",
// we're enabling this so if player has the key,
// they can unlock the top drawer without being
// prompted for the key
allow_inference_on_first_use: true,
});
WorkingWithCollections.createAsset({
class: "Player",
name: "Explorer",
place: { in: "Office" },
});
WorkingWithCollections.createAsset({
class: "Room",
name: "Office",
description: `
This demo features several collection examples,
including basic and advanced descriptions. Examine the
<b>furnishings</b>, the <b>desk drawers</b>, and once
you've opened some drawers, the <b>papers</b> inside them.
Examine things again after opening and closing the drawers
and moving the papers around.
`,
exits: {},
});
// --------------------------------------------------
// Desk and Drawers
// --------------------------------------------------
WorkingWithCollections.createAsset({
class: "Desk",
name: "desk",
place: { in: "Office" },
descriptions: {
look: {
default: `It's a heavy wooden desk, with an old office chair. It has three drawers stacked vertically. 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 }. `,
},
},
adjectives: ["rolling", "wooden", "heavy"],
// is: { listed: false },
});
WorkingWithCollections.createAsset({
class: "Drawer",
name: "top drawer",
synonyms: "top desk drawer, top drawer lock, lock",
description: "The top drawer is { top drawer [is] open [or] closed }. ",
adjectives: "desk",
place: { attached: "desk" },
is: { closed: true, locked: true, listed: false },
dov: { unlock: { with_assets: ["tiny brass key"] } },
});
WorkingWithCollections.createAsset({
class: "Drawer",
name: "middle drawer",
synonyms: "middle desk drawer",
descriptions: {
look: {
default: "The middle drawer is { middle drawer [is] open [or] closed }. ",
},
in: { default: "YOU LOOKED IN THE DRAWER! " },
},
adjectives: "desk",
place: { attached: "desk" },
is: { closed: true, listed: false },
});
WorkingWithCollections.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: "desk" },
is: { closed: true, listed: false },
});
WorkingWithCollections.createAsset({
class: "Collection",
name: "desk drawers",
place: { attached: "desk" },
targets: "top drawer, middle drawer, bottom drawer",
synonyms: ["drawers", "three drawers"],
is: { listed: false },
// We could write a simple sentence for each drawer...
// description: `
// The bottom drawer is { bottom drawer [is] open [or] closed }.
// The middle drawer is { middle drawer [is] open [or] closed }.
// The top drawer is { top drawer [is] open [or] closed }.
// `,
// ...but that's boring.
// Instead, we're going to be fancy and use
// some custom code to write a smarter sentence.
description: function () {
let open_count = [];
let closed_count = [];
let msg =
"The desk has three drawers stacked vertically, top, middle and bottom. ";
// make two lists for open & closed drawers
WorkingWithCollections.$("top drawer").is.closed
? closed_count.push("top drawer")
: open_count.push("top drawer");
WorkingWithCollections.$("middle drawer").is.closed
? closed_count.push("middle drawer")
: open_count.push("middle drawer");
WorkingWithCollections.$("bottom drawer").is.closed
? closed_count.push("bottom drawer")
: open_count.push("bottom drawer");
// use some if statements to build a sentence
// based on the lengths of our two lists
if (0 === open_count.length) {
msg += "All three are closed.";
} else if (0 === closed_count.length) {
msg += "All three are open.";
} else if (2 == open_count.length) {
msg += `The ${open_count[0]} and the ${open_count[1]} are open. The ${closed_count[0]} is closed. `;
} else if (2 == closed_count.length) {
msg += `The ${closed_count[0]} and the ${closed_count[1]} are closed. The ${open_count[0]} is open. `;
}
return msg;
},
});
// --------------------------------------------------
// Chair
// --------------------------------------------------
WorkingWithCollections.createAsset({
class: "Chair",
name: "chair",
place: { in: "Office" },
dov: { turn: true },
description: "It's an old fashioned wooden office chair.",
adjectives: "wooden, office",
// By default, a player who is nested can't reach
// outside the area of their nest object.
// This explicitly lets the player reach the desk
// while seated on the chair.
aspects: { on: { nest: { can: { reach: ["desk"] } } } },
});
// --------------------------------------------------
// Couch
// --------------------------------------------------
WorkingWithCollections.createAsset({
class: "Couch",
name: "sofa",
place: { in: "Office" },
description:
"It's an inviting Chesterfield sofa, clad in warm polished leather.",
adjectives: ["inviting", "leather"],
});
// --------------------------------------------------
// Furnishings
// --------------------------------------------------
WorkingWithCollections.createAsset({
class: "Collection",
name: "furnishings",
targets: ["desk", "chair", "sofa"],
place: { in: "Office" },
description:
"A mahogany desk, a rolling office chair, and a Chesterfield sofa. ",
});
// --------------------------------------------------
// Key
// --------------------------------------------------
WorkingWithCollections.createAsset({
class: "Key",
name: "tiny brass key",
description: "It's a tiny key, made of brass. ",
article: "a",
adjectives: ["tiny", "brass"],
place: { on: "Desk" },
});
// --------------------------------------------------
// Papers
// We're putting a colored piece of paper in each drawer.
// Collections are really intended for fixed assets that
// don't move around, because collections aren't global
// and don't automatically account for items that aren't
// present. However, we CAN make a collection for moveable
// assets if we really want to.
// --------------------------------------------------
WorkingWithCollections.createAsset({
class: "Paper",
name: "pink paper",
adjectives: ["pink"],
synonyms: ["paper"],
description: "It's a sheet of pink paper. ",
place: { in: "top drawer" },
});
WorkingWithCollections.createAsset({
class: "Paper",
name: "blue paper",
adjectives: ["blue"],
synonyms: ["paper"],
description: "It's a sheet of blue paper. ",
place: { in: "middle drawer" },
});
WorkingWithCollections.createAsset({
class: "Paper",
name: "goldenrod paper",
description: "It's a sheet of goldenrod paper. ",
place: { in: "bottom drawer" },
});
WorkingWithCollections.createAsset({
class: "Collection",
name: "papers",
// Collections aren't global, they have to be in a room somewhere,
// though you could do something like attach them to the player.
place: { in: "Office" },
targets: "pink paper, blue paper, goldenrod paper",
synonyms: ["papers", "three papers"],
is: { listed: false },
// We're not even going to try writing a simple sentence
// for these papers, because they might be hidden
// or not even in the room.
// description: `
// The pink paper is { pink paper [is] ? [or] ? }.
// The blue paper is { blue paper [is] ? [or] ? }.
// The goldenrod paper is { goldenrod paper [is] ? [or] ? }.
// `,
// In this case, we have no choice
// but to code a smart response.
description: function () {
let msg = "";
let papers = ["pink paper", "blue paper", "goldenrod paper"];
// OPTION A
// This version will result in output like:
// "A sheet of blue paper is here.
// A sheet of pink paper is here, on the desk.
// A sheet of goldenrod paper is here, on the desk. "
// // loop through papers
// for (let i in papers) {
// let paper = WorkingWithCollections.$(papers[i]);
// if (paper.is.visible) {
// msg += `A sheet of ${paper.name} is here`;
// let place = paper.getPlaceAsset();
// if (place.name !== "Office") {
// let prep = paper.getPlacePreposition();
// msg += `${prep} ${place.article_name}`;
// }
// msg += `.`;
// }
// }
// Once again, that version is pretty boring.
// OPTION B
// This version will output something like:
// "A sheet of pink paper and a sheet of blue paper
// are on the desk, and a sheet of goldenrod paper
// is in the top drawer."
// make a list of visible papers
let visible = [];
// make a separate list of carried papers
let carried = [];
// loop through the papers
for (let i in papers) {
let paper = WorkingWithCollections.$(papers[i]);
// we're going to distinguish between papers
// carried by the player and those in the room
if (paper.is.carried) {
carried.push(paper);
} else if (paper.is.visible) {
visible.push(paper);
}
}
if (!visible.length && !carried.length) {
msg = `You don't see any papers here. `;
}
if (carried.length) {
msg += `You're carrying `;
const count = carried.length;
for (let i = 0; i < count; i++) {
let paper = carried[i];
msg += `a sheet of ${paper.name}`;
if (count > 1 && i < count - 1) msg += `, `;
if (count > 1 && i === count - 2) msg += ` and `;
}
msg += `. `;
}
if (visible.length) {
msg += `You can see `;
const count = visible.length;
for (let i = 0; i < count; i++) {
let paper = visible[i];
let paper_parent = paper.getPlaceAsset();
let paper_preposition = paper.getPlacePreposition();
msg += `a sheet of ${paper.name} `;
if (paper_parent.id === "Office") {
msg += ` here`;
} else {
msg += `${paper_preposition} ${paper_parent.article_name}`;
}
if (count > 1 && i < count - 1) msg += `, `;
if (count > 1 && i === count - 2) msg += ` and `;
}
msg += `. `;
}
return msg;
},
});
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
Example Code
Here are several basic collection scenarios to get you thinking about
potential uses. Take note of the targets property of these
assets. In this first example set in a cobblestoned room, we want to draw
attention to three specific cobblestones.
// here we have several similar cobblestones
MyGame.createAsset({
class: "Collection",
name: "cobblestones",
targets: "pitted cobblestone, weathered cobblestone, cracked cobblestone",
description: "The entire room seems to be laid in cobblestones. Not just the floor, but also the walls and ceiling. The stones around the frame of the wood planked door draw your eye. Three in particular stand out: a cracked cobblestone, a weathered cobblestone, and a pitted cobblestone. ",
place: { in: "Dungeon Antechamber" },
is: { listed: false, known: true },
});
This sink has two faucets and we want each one to be an individual asset that can be manipulated, but we also want the player to be able to examine faucets.
MyGame.createAsset({
class: "Collection",
name: "sink handles",
targets: ["sink's hot water handle", "sink's cold water handle"],
description: "Two porcelain sink handles, hot and cold. ",
synonyms: ["porcelain handles"],
place: { attached: "vessel sink" },
is: { listed: false },
});
This furnishings collection lets the player examine all the furnishings in the room. We're deliberately using the word furnishings instead of furniture because Furniture is a class, and examine furniture will prompt for which instance of the class we meant.
MyGame.createAsset({
class: "Collection",
name: "furnishings",
targets: "uncomfortable looking bed, desk, table",
description: "A bed, a desk, and a table. ",
place: { in: "Hotel Room" },
});
This example defines a collection that shows a description for three distinct drawers of a desk.
MyGame.createAsset({
class: "Collection",
name: "desk drawers",
synonyms: ["drawers", "three drawers"],
place: { attached: "desk" },
targets: "top drawer, middle drawer, bottom drawer",
description: "There are three drawers in the desk: top, bottom, and middle. ",
is: { listed: false },
});
Advanced Example
Next we want to expand our desk drawers example, by setting the collection's description to show the state of each of the three drawers it describes. We will use placeholders to print whether each drawer is open or closed, and using a bit of custom code, we can build on that to write a slightly more nuanced description.
MyGame.createAsset({
class: "Collection",
name: "desk drawers",
place: { attached: "desk" },
synonyms: ["drawers", "three drawers"],
targets: "top drawer, middle drawer, bottom drawer",
description: function () {
let open_count = [];
let closed_count = [];
let msg =
"The desk has three drawers stacked vertically. ";
// make two lists for open & closed drawers
MyGame.$("top drawer").is.closed
? closed_count.push("top drawer")
: open_count.push("top drawer");
MyGame.$("middle drawer").is.closed
? closed_count.push("middle drawer")
: open_count.push("middle drawer");
MyGame.$("bottom drawer").is.closed
? closed_count.push("bottom drawer")
: open_count.push("bottom drawer");
// use some if statements to build a sentence
// based on the lengths of our two lists
if (0 === open_count.length) {
msg += "All three are closed.";
} else if (0 === closed_count.length) {
msg += "All three are open.";
} else if (2 == open_count.length) {
msg +=
`The ${open_count[0]} and the ${open_count[1]} are open, while the ${closed_count[0]} is closed. `;
} else if (2 == closed_count.length) {
msg +=
`The ${closed_count[0]} and the ${closed_count[1]} are closed, while the ${open_count[0]} is open. `;
}
return msg;
},
});
Now our output might look something like this:
Further Reference
- Collections asset extension in Reference Manual
- Placeholders in Author's Manual