NPCs: Allowing the player to give commands - ThePix/QuestJS GitHub Wiki

By default, the player can tell an NPC to do something, and the NPC will just do it. This will work for all the built-in commands except LOOK, LOOK AT and SPEAK TO, and is relatively easy to implement for custom commands too.

However, you probably do not want your NPCs to be compliant robots. They will be more interesting if they sometimes say no.

One Function

We will make a character who always say no first, then introduce some variation.

createItem("Lara", NPC(true), {
  loc:"dining_room",
  examine:"A normal-sized bunny.",
  happy:false,
  getAgreement:function() {
    msg("'I'm not doing that!' says Lara indignantly.");
    return false;
  },
});

The "getAgreement" function is a default for all actions. If it returns true the character will do everything, if it returns false, she will refuse to do anything. As with all these functions, you must provide a message to give the player a response if returning false (you could if returning true too, perhaps the NPC saying "Okay", but Quest will report the NPC doing the action, so it is optional).

The "getAgreement" function will be sent the command type, and other parameters, depending on what the command type is.

Type Other parameters Comments
Take object Also when taking out of a container
Drop object
Drop/in object, container
Go exit
Fill vessel, fluid Also used for emptying, in which case fluid is undefined
Posture posture
Give object, receiver
Follow Always agree to wait
SwitchOn object, switching on?
Push object, direction

Many functions

Alternatively, you can selectively give a "getAgreement" function for specific command types. The name to use is "getAgreement" plus the type of the command, as in the list above. It will be sent additional parameters, again as per the list above.

In this example, Lara will only follow a command telling her to go to another room if the attribute "happy" is true. Note that this will not work if we have given the NPC a "getAgreement" function. NPCs here a default "getAgreement" function that checks for the individual "getAgreement[x]" functions - f yo give the NPC a "getAgreement" function that is not going to happen.

createItem("Lara", NPC(true), {
  loc:"dining_room",
  examine:"A normal-sized bunny.",
  happy:false,
  getAgreementGo:function(exit) {
    if (!this.happy) {
      msg("'I'm not going " + exit.dir + ",' says Lara indignantly. 'I don't like that room.'");
      return false;
    }
    return true;
  },
});

This example will have Lara refuse to pick up bricks, but will pick up other objects:

    getAgreementTake:function(item) {
      if (item === w.brick) {
        msg("'I'm not picking up any bricks,' says Lara indignantly.");
        return false;
      }
      return true;
    },

Note on custom commands

You can plug your own commands into this system. Commands should use the former, one-function system, as this will ensure they work either way.

This example is from the SWITCH OFF command. It uses "getAgreement", sending the command category, then the relevant parameters. If the function returns false, the command is aborted at this point.

    if (!char.getAgreement("SwitchOn", this, false)) return false

You can use your own command types or the existing ones.

Note on following

If an NPC is following the player (or another NPC), before entering each room the character's "testFollowTo(room)" function will be run. This can be used to stop the NPC going in certain rooms.

Using this example, you can flag a room with noFollow:true, to stop this NPC following the player into that room. The user will need to tell the NPC to start following again.

  testFollowTo:function(exit) {
    const room = w[exit.name]
    if (!room.noFollow) return true
    msg("You realise {nv:char:be} no longer following you.", {char:this})
    this.setLeader()
    return false
  },

Alternatively we can test for an attribute on the exit.

  testFollowTo:function(exit) {
    if (!exit.noFollow) return true
    msg("You realise {nv:char:be} no longer following you.", {char:this})
    this.setLeader()
    return false
  },

Note on long range communication

Where telling an NPC to do stuff could be especially useful is if the player cannot do it herself. For example, the NPC could be half way round the world, talking on the phone. To allow the player to give commands (and to speak) to an NPC in another room, just have the "canTalkToPlayer" function return true.

    canTalkToPlayer:function() { return true; },

This could be made more elaborate by having "canTalkToPlayer" depend on whether the player is currently on the phone to that NPC.