Tuesday, September 18, 2012

How could I do NPC scripting II

A while ago I already wrote an article about how I could define NPC dialogs with JSON. Looking back at it now it's just awful. It's convoluted, error-prone, unreadable and not even very flexible. I got a Javascript engine, so why not do as much with Javascript as possible?

    
    EVENT_0 {
        this.text = 'Hello.<br/>How can I help you?';
        this.portrait = 'girl.png';
        
        this.addOption('Where is the castle?', 1);
        this.addOption('I need a job', 2);
        this.addOption('Nothing, goodbye', 3);
    }

    EVENT_1 {
        this.text = 'Just go down the road, you can\'t miss it.';
    }

    EVENT_2 {
        switch(user.getVar('quest_slay_dragon')) {
            undefined:
                this.text = 'I got a quest for you.<br/>Kill the dragon in the mountain.';
                user.setVar('quest_slay_dragon', 1);
                break;
            1:
                this.text = 'Did you already slay the dragon?';
                this.addOption('Errr...');
                this.addOption('Well...');
                this.addOption('Uh...');
                break;
            2:
                this.text = 'Great, you killed the dragon. Here is your reward.';
                user.addMoney(10000);
                user.setVar('quest_slay_dragon', 3);            
                break;
            3:
                this.text = 'Sorry, I got no more work for you.
                break;
        }
    }

    EVENT_3 {
          this.text = 'It was a pleasure to meet you.';
          if (user.getVar('quest_slay_dragon') == 1)
              this.text += ' And good luck with the dragon.';
    }

This does even more than the JSON dialog before with less lines. It is not only much more readable, it also allows to utilize the full power of Javascript to make anything conditional or procedurally generated.

As any reader with Javascript knowledge will notice,
EVENT_0 {
isn't valid Javascript syntax.The great thing is that I can do some preprocessing on the Java side before passing the code to Rhino. So I can expand it to:

    
    event[0] = new Event();
    event[0].init = function(user) {

So how does the dialog system work?

Each NPC dialog is an array of Event objects. Each Event object gets an additional init function after creation. When a player talks to an NPC, the server calls event[0].init to set up some variables inside the event object. Then it sends the event to the client. When the init function added one or more dialog options using this.addOption, the client can reply with the option it wants to use. When it does, the event with the event number passed to the corresponding addOption() call is processed.

The Event object:

text: HTML which is shown in the main body of the NPC dialog window.
portrait: URL of an image which is shown in the corner of the NPC dialog. When undefined, the portrait of the last event is used.
addOption(text, event): makes the client shows text as a hyperlink at the end of the dialog text. When the user clicks on this link, the client informs the server about which option was selected. The server then processes the corresponding event. When event is undefined, the dialog ends when this option is selected.
next: Event which is processed next when no option is selected. This changes the 'quit' button below the dialog window to a 'next' button.

No comments:

Post a Comment