Skip to content

Basic Script Examples

y785 edited this page Nov 1, 2019 · 3 revisions

Any script action that results in a ScriptMessage packet being sent to the client (say, askMenu,... etc) must be nested inside of the previous script action. Scripts do not block thread flow, anything after first say will be executed immediately after.

// The client doesn't expect 2 script messages at once, 
// which results in the client crashing from error 38.
say("1234"); // Sends "1234" to client
say("5678"); // Sends "5678" to client

SpeakingScript::say

Say do:

While this will work correctly, see the advanced below for a better example of sending multiple say messages in a single line.

protected void work() {
    // Lorem is sent, and when there is a continuation reply from the client Ipsum is sent.
    say("Lorem").andThen(
        () -> say("ipsum"));
}
Say dont:
protected void work() {
    say("Lorem"); // sends the say packet
    say("ipsum"); // sends another say packet, before a response.
}
Say better:
protected void work() {
    // Sends the say message with back/next properly set, where
    // "Lorem" is the first say message and "sit amet" is the last.
    say("Lorem", "ipsum", "dolor", "sit amet").andThen(() -> { 
        // All nested blocks are executed after the last message.
        // Think of them as runnables to be executed only if the
        // script reaches this point of execution.
    });
}
Using script string builder
protected void work() {
        var sb = new ScriptStringBuilder();
        var id = getSpeakerTemplateId();

        sb.append("Hello, my script is missing ^-^.").newLine();
        sb.append("Script: ").red().append(expected).black().newLine();
        sb.append("Field: ").purple().append(getFieldObject().map(FieldObject::getId).orElse(0)).black().newLine();
        sb.append("Npc: ").blue().append(id).black().newLine();
        sb.append("If you know how this NPC works, feel free to contact staff. The goal is to have all NPCs GMS-like. I'm working as fast as I can, but there are hundreds of scripts to write. Sorry for the inconvenience. ^-^");
        say(sb.toString());
}

SpeakingScript::sayf

SpeakingScript::sayf is the same as calling SpeakingScript::say(MoeMatter.format(first, objects))

protected void work() {
    // is the same as say("Lorem ipsum dolor sit amet");
    sayf("Lorem {} {} {} {}", "ipsum", "dolor", "sit amet");
}

SpeakingScript::askMenu

AskMenu do:

This will send a menu array of "Lorem", "ipsum", "dolor", "sit", "amet" to the client. When the client selects an item, the response is returned in the nested block as an Integer.

protected void work() {
    // This will send a menu array of ["Lorem", "ipsum", "dolor", "sit", "amet"]
    askMenu("How much do you want?", "Lorem", "ipsum", "dolor", "sit", "amet").andThen(selection -> {
        // Selection is an integer index of whatever was selected above.
    });
}
AskMenu dont:

This will work the same as above, but it's ugly and prone to errors. Any custom formatting needs can be added onto the above like normal. If you don't want the default blue color on the list, you can use the ScriptStringBuilder.

protected void work() {
    askMenu("How much do you want?#L1#Lorem#l#L1#ipsum#l#L1#dolor#l#L1#sit#l#L1#amet#l").andThen(selection -> {
        // Ugly, hard to read
    });
}
AskMenu better:

By using a MenuItem class we eliminate the need to know the returned selection index. You can also pass them as askMenu(String: prompt, List<MenuItem> items) for easy custom menus.

protected void work() {
    askMenu("How much do you want?",
        MenuItem.of("Lorem", () -> {
            // Do something when "Lorem" is selected.
        }),
        MenuItem.of("ipsum", this::something),
        MenuItem.of("dolor", this::somethingElse),
        MenuItem.of("sit", this::work),
        MenuItem.of("amet") // Do nothing on selection.
    );
}

SpeakingScript::askYesNo / SpeakingScript::askAccept

SpeakingScript::askAvatar

Coming soon...