Tutorial 9: Complex mechanisms - ThePix/QuestJS GitHub Wiki

... Continued from Commands.

At the moment, there is no charger item. As long as the player is in the garage, the torch can be charged (just to be clear, this is a torch in the British sense, what in the US would be called a flashlight, an electrical device with batteries).

Let us suppose the charger is made up of two sub-components, a compartment and a button plus the charger itself. To charge the torch, the player needs to put the torch in the compartment, close it and then press the button. The charger itself is simple enough.

createItem("charger", {
  loc:"garage", 
  examine: "A device bigger than a washing machine to charge a torch? It has a compartment and a button.", 
})

We can use the COMPONENT template to create a component. We will do the compartment first, as it is easiest. It is a container, so needs that template too. The description uses the text processor to show its state:

createItem("charger_compartment", COMPONENT("charger"), CONTAINER(true), {
  alias:"compartment",
  examine:"The compartment is just the right size for the torch. It is {if:charger_compartment:closed:closed:open}.",
})

The button is more complicated as we need to give is a "push" attribute, which will fire when the player pushes it. This checks that the torch is in place and the compartment is closed. If so, the torch is charged.

createItem("charger_button", COMPONENT("charger"), {
  examine:"A big red button.",
  alias:"button",
  push:function(options) {
    if (!w.charger_compartment.closed || w.torch.loc !== "charger_compartment") return falsemsg("{pv:char:push:true} the button, but nothing happens.", options)

    msg("{pv:char:push:true} the button. There is a brief hum of power, and a flash.", options)
    w.torch.power = 20
    return true
  },
})

That should now work. You may want to remove the CHARGE command, as that shortcuts the whole system we are building here.

Improving It

We can add a couple of improvements. Firstly, we will only allow the player to put a single item in the compartment. You can have a container restrict what goes into it by giving it a "testDropIn" function. This will get sent a set of options, including options.item, the object that is to go in the container. You could use it to stop specific items going in. We want to restrict the number, so we get the current contents with the "getContents" attribute, and if the length of that list is more than zero, we give a message and return false.

createItem("charger_compartment", COMPONENT("charger"), CONTAINER(true), {
  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
  },
})

We can also have the charger description include the state of the compartment. There are various ways to do that, the most obvious being to have the "examine" attribute a function. Instead, we will create a new text processor command. This is really to show how it is done; it is overkill in this situation.

Here is the code, it can go in the data.js file, I suggest just after the charger components, or in code.js.

tp.addDirective("charger_state", function() {
  if (w.charger_compartment.closed) {
    return "The compartment is closed"
  }
  const contents = w.charger_compartment.getContents()
  if (contents.length === 0) {
    return "The compartment is empty"
  }
  return "The compartment contains " + formatList(contents, {article:INDEFINITE, lastJoiner:'and'})
})

What is does is call a function tp.addDirective, sending it two things; the name and the function, which must return a string. We can then access it in any string:

createItem("charger", {
  loc:"garage",
  examine: "A device bigger than a washing machine to charge a torch? It has a compartment and a button. {charger_state}.", 
})

... Concludes in Uploading