aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Metin2

Metin2 NPC Dialogue: Branching with select and confirm

A Metin2 NPC dialogue begins the moment a player opens the chat window with an NPC, and when it is built well it feels like a small story. Showing a single line of text is easy; the real work is offering the player choices and branching the conversation based on the decision they make. In this article I walk through building multi-choice menus with select, yes/no prompts with confirm, and how to combine them into a clean, working Lua quest step by step.

The basic dialogue skeleton

Every NPC conversation opens with a chat trigger. You write the NPC's vnum, give the window a title, and lay out the text line by line with say functions.

quest villager_dialogue begin
    state start begin
        when 9003.chat."Talk to the villager" begin
            say_title("Old Villager")
            say("Welcome, traveller.")
            say("")
            say("What brings you here?")
        end
    end
end

Here 9003 is the NPC's vnum, say_title is the header of the dialogue window, and say is the body text. An empty say("") is used to leave a blank line. This block is one-way: the player only reads and clicks "Close". To add interaction we need the selection mechanisms.

Multi-choice menus with select

select shows the player a list of buttons and returns the index of the chosen option (starting at 1). You capture the returned value in a variable and branch on it with if / elseif.

when 9003.chat."Talk to the villager" begin
    say_title("Old Villager")
    say("How can I help you?")
    say("")
    local choice = select("Give me a quest", "Buy goods", "Tell your story", "Never mind")

    if choice == 1 then
        say("Then listen: clear out the wolves in the forest.")
    elseif choice == 2 then
        say("Sadly, I have nothing left to sell.")
    elseif choice == 3 then
        say("Years ago, this village was a very different place...")
    else
        say("Safe travels.")
    end
end

A few things to watch: in practice select is comfortable up to about 6-7 options; any more and they won't fit the dialogue window. Making the last option a "Never mind" exit is a good habit so the player never gets stuck in the menu. Always store the returned value in a local variable, because if you call a second select the previous value is lost.

Yes/no confirmation with confirm

Before an irreversible action (deleting an item, teleporting, spending yang) you want to ask the player for confirmation. confirm("text") is exactly for this: it opens a "Yes / No" window for the player and returns true if they accept and false if they decline.

when 9003.chat."Teleport to the city" begin
    say_title("Old Villager")
    say("Shall I send you to the city?")
    say("This costs 50,000 yang.")
    say("")

    if confirm("Do you want to teleport to the city?") then
        if pc.get_money() >= 50000 then
            pc.change_money(-50000)
            pc.warp(469000, 964000)
        else
            say("You don't have enough yang.")
        end
    else
        say("Maybe another time.")
    end
end

confirm can be used directly inside an if condition because it returns a boolean. Some versions also offer confirm_with_button("question", "Yes label", "No label") for custom button text; however, since its availability depends on your source, stick with the standard confirm if you want to stay portable.

Chaining select and confirm together

The real power appears when you nest the two. The player first picks from a select menu, then commits to their final decision with confirm at the critical step. Below is a reward-chest NPC example:

when 9003.chat."Reward chest" begin
    say_title("Mysterious Chest")
    say("Choose a reward from inside, but be careful.")
    say("There is no going back after you pick.")
    say("")
    local reward = select("Sword", "Armour", "Yang", "Later")

    if reward == 4 then
        say("The chest closed.")
        return
    end

    if not confirm("Are you sure about your choice?") then
        say("You paused for a moment over the chest.")
        return
    end

    if reward == 1 then
        pc.give_item2(11209, 1)
        say("You earned a sharp sword!")
    elseif reward == 2 then
        pc.give_item2(11949, 1)
        say("You earned sturdy armour!")
    elseif reward == 3 then
        pc.change_money(500000)
        say("You earned 500,000 yang!")
    end
end

Three techniques work together here: select determines the branch, an early return cleanly handles the "never mind" case, and confirm places a final safety gate before handing out the irreversible reward. Using return keeps the code out of deep if nesting and easy to read.

Making text dynamic

Static text gets dull fast. To weave the player's name, level, or item count into the text, use string concatenation inside say. In Lua the concatenation operator is two dots (..).

when 9003.chat."Greet" begin
    say_title("Old Villager")
    say("Hello "..pc.get_name()..", good to see you.")
    say("You're level "..pc.get_level()..", you've progressed fast.")
end

This makes the same dialogue feel personal to every player. When you mix numeric values (pc.get_level()) into text, Lua converts them automatically, so you don't even need to call tostring().

Common mistakes and tips

  • Forgetting to check the select result: the player can close the window with ESC; define a branch for every option plus a "never mind" exit.
  • Writing confirm like a plain if and forgetting the parentheses: confirm(...) always needs a text argument, don't leave it empty.
  • Very long say chains: after 4-5 lines, show the player a "Continue" button with wait(); otherwise the text window overflows.
  • Special characters: save quest files in the encoding the server expects (usually UTF-8 or the relevant locale); the wrong encoding shows up as garbled characters in the dialogue.
  • Don't forget to compile and reload the quest after a change; otherwise the game core keeps showing the old text.

Frequently Asked Questions

How many options does select support?

There is no hard technical limit, but in practice 6-7 options fit comfortably in the dialogue window. If you need more, split the options into categories: show the groups in the first menu, then open a second select based on the chosen group. This looks cleaner and stays readable.

Can I change the button text in the confirm window?

The standard confirm uses fixed "Yes/No" buttons. Some sources include an extended version that lets you set custom button text, but that depends on your version. If you want portability, the safest path is to phrase the actual question clearly with say and keep confirm simple.

Can I use the select result directly inside an if?

You can, but it isn't recommended. select shows the menu and returns the value; if you repeat the same call in two if branches, the menu opens twice. Instead, capture the result in a local variable, then compare that variable.

Stuck on NPC dialogues? If you need help with branching quest chains, multi-step dialogue systems, or menu design for your server, get in touch with me — let's build it together.

Bu kategorideki tüm yazılar →

Devamı için