Template: CONTAINER and SURFACE - ThePix/QuestJS GitHub Wiki

Containers and surfaces are items that can hold other items. A box, sack or chest would be a container; items go inside it. A shelf or table could be a surface; items go on top of a surface.

CONTAINER

The CONTAINER template should be sent a Boolean, indicating if it can be opened and closed. A container that can be opened defaults to starting closed. A container that cannot be opened and closed defaults to being open.

Here is an example of a container that can be picked up, and starts the game open.

createItem("jewellery_box", TAKEABLE(), CONTAINER(true), { 
  loc:"glass_cabinet",
  examine:"A nice box.",  
  closed:false,
});

To make a container locked, you can use the LOCK_WITH template, discussed here.

container, closed, locked, transparent, openable

Boolean flags with obvious meanings. Note using "closed" instead of "isopen" as in Quest 5 so we can still use "open" and "close" as verbs.

canReachThrough(), canSeeThrough()

Functions return Booleans. Rather than checking the state of the Boolean flags above, it is better practice to use these to determine whether the contents can be see or reached.

getContents()

Gets the contents of the container, an array of items.

listContents()

Gets the contents of the container as a single string.

testKeys(char, toLock)

Checks if the given character has the key (or keys) required to open this container. The "toLock" parameter should be true if the item is to be locked, false otherwise.

testDropIn(options) and testTakeOut(options)

Checks if the container will allow the given character to put the given object in it or take it out. If not, it should give a message and return false. The options parameter is a dictionary with "char" set to the character", "item" to the item.

See also...

Containers use the OPENABLE template to handle opening/closing; more details here.

SURFACE

A surface (an object things can be put on, such as a table) is similar to a container that cannot be closed. The getContents() and listContents() functions work as they do for containers.

Surfaces are very easy to set up, as this example shows.

createItem("big_kitchen_table",  SURFACE(), {
  loc:"kitchen",
  examine: "A Formica table.",
});

Notes

Recursion

By recursion, I mean putting one container inside another, when the second container is already insider the first. Suppose we have a box, and inside the box there is a bag. How do we handle:

PUT BOX IN BAG

Containers and surfaces have a "testRecurision" function that will check for this. Note that it relies on a container having a "loc" property set for any container or surface. This means that a container that straddles two rooms may have issues, and you may need to modify its "testRecurision" function to take account of that.

Takeable containers

Note that takeable containers must have a "loc" attribute so Quest can determine exactly what a character is carrying. If the container cannot be picked up, this is not required, and you can use a custom "isAtLoc" function.

Limiting

Sometimes you want to limit what can be put in a container or on a surface. This example from the tutorial illustrates doing that with the "testDropIn" function attribute (whether a surface or container), limiting it to just one item.

createItem("charger_compartment", COMPONENT("charger"), CONTAINER(false), {
  alias:"compartment",
  examine:"The compartment is just the right size for the torch. It is {if:charger_compartment:closed:closed:open}.",
  testDropIn:function(options) {
    const contents = w.charger_compartment.getContents();
    if (contents.length > 0) return falsemsg("The compartment is full.")

    return true
  },
})

This example limits it to a specific item.

createItem('hatstand', SURFACE(), {
  examine:'The hatstand is wooden, and badly made; it is a wonder it is still together.{if:floppy_hat:loc:hatstand: There is a floppy hat on it.}',
  loc:'theatre',
  scenery:true,
  testDropIn:function(options) {
    if (options.item !== w.floppy_hat) return falsemsg("You can only put hats on the hatstand - the clue is in the name.")
    return true
  },
})