Social Studio ES|EN
NPC dialogues

UltraDialogue v1.1.3

Dialogue screens with branching answers, conditions and actions. No resource pack.

UltraDialogue is a Paper plugin by Social Studio (GupeLegend) that turns any NPC — or any entity — into a character with real conversations: a portrait, what it says, and the player's replies as on-screen buttons. It uses the native dialogue screens Minecraft introduced in the 1.21.6 client, so it needs no mod and, in its basic presentation, no resource pack either. Every reply can branch the conversation, open a menu, give or take items, change the player's affinity with that character, or run any command.

What it is

UltraDialogue started out as "Dialogos", a private plugin built for a single server, and in October 2026 it was rewritten and released as UltraDialogue, open source under the MIT license. The goal is simple: building a conversation with branches, conditions and rewards should be as easy as writing a YAML file, with no need for an external graphical editor or a client mod.

Each dialogue is a .yml file inside the dialogues/ folder; the file name is the dialogue's id. Inside it, a series of nodes represent each turn of the conversation: what the character says and the buttons the player answers with. Replies can lead to another node, open a menu, run commands, change affinity, give items or complete quests — and all of it can be conditioned on permissions, flags, affinity, items in the inventory, or any PlaceholderAPI placeholder.

Note: UltraDialogue doesn't draw NPCs or build scenery. The NPCs, the world, the quests, the Codex and any other resource-pack customization visible in this page's screenshots belong to the server where they were taken — they don't ship with the plugin.

Features

  • Branching conversations in YAML: nodes, replies, and where each one leads.
  • Actions on any reply: jump to another node or dialogue, open a menu, run a command as the player or as console, send a message / title / action bar, play a sound.
  • Button sizes and icons: four button sizes just like the pause menu's (from the small square up to full width), and a set of ten quest-style symbols — quest, main/side/riddle marker, accept, back, exit, reward, story and chance. They're text, so no resource pack is needed.
  • The NPC's face: a 2D face taken from the NPC's skin, next to its name.
  • Free rows (layout: [1, 2, 2, 1]): any number of buttons per row, something Minecraft's native grid can't do on its own.
  • RPG buttons: with the plugin's optional button pack (which it offers on join), those buttons are drawn as a panel with a flat dark background and a double frame colored by its function. Without the pack, they look like [ text ].
  • Locked replies and navigation: replies that can't be picked yet show in gray with a hint, a Back button that remembers the path taken, and a fixed Exit at the bottom, plus per-node titles.
  • Deliveries: check, take and give items — vanilla, UltraBoss trophies, or any item by its hidden tag, its model or its name. Taking is all-or-nothing and re-checked on click, so there's no duplication.
  • Story and quest gating: conditions on BeautyQuests quests (quest: completed 12), its own BeautyQuests stage type, and affinity and flag placeholders.
  • Conditions: hide replies or change where the conversation starts — permissions, player flags, and any PlaceholderAPI placeholder (%vault_eco_balance% >= 1000).
  • Player flags: remember that someone already met a character, took a job, or finished a step, so they get greeted differently next time. They're stored on the player itself, with no database.
  • Affinity: a hidden friendship level per player and per character. Kind replies raise it, rude ones lower it, and as it grows it unlocks new greetings, questions and secrets. The player never finds out it changed.
  • Conversations that never repeat the same way: several variants per line (a different one each time) and rotating questions, so talking to the same NPC twice doesn't feel scripted.
  • Portraits: the NPC's own skin, any player's skin, a fixed texture, or an item.
  • FancyNpcs support out of the box: linking a dialogue to an NPC by its name makes a click open the conversation instead of its FancyNpcs actions. Your npcs.yml is never touched.
  • Any entity can talk, with a tag: /tag @e[type=villager,limit=1,sort=nearest] add dialogue:merchant.
  • Older clients coming in through ViaVersion (pre-1.21.6) get the same conversation in chat, with clickable replies.
  • English and Spanish included out of the box — and, as explained below, also in the dialogue files' own keys.

Requirements and compatibility

Paper 26.1.2 (verified server target)
Java 25
api-version 26.1.2
Folia Not supported (folia-supported: false)
Bedrock / Geyser Not verified

The chat fallback for older clients (via ViaVersion) does not imply compatibility with server versions older than 26.1.2: it only changes how an old client receives the dialogue, not which server runs it.

Everything else is optional and detected at runtime:

Plugin What it's used for
FancyNpcs Clicking an NPC opens its dialogue
PlaceholderAPI Placeholders in text, actions and conditions
DeluxeMenus The menu: action (any menu plugin works by changing menu-command)
ViaVersion Sends older clients the chat version
BeautyQuests 2.1 Quest conditions and actions, the ULTRADIALOGUE stage type

Important: these five plugins are listed as softdepend in plugin.yml, so UltraDialogue starts up fine without any of them. If one is missing, the part that depends on it simply stays inactive (for example, quest conditions are always false without BeautyQuests), and the console warns about it once per dialogue.

Installation

  1. Drop the .jar into plugins/ and start the server. On first startup, three example dialogues are created (example.yml, merchant.yml and kadir.yml).
  2. The plugin's data lives in plugins/UltraDialogue/, its own regular Bukkit folder. If you're coming from a version that still used the shared plugins/UltraView/UltraDialogue/ folder, the plugin moves everything automatically the first time it starts (it announces this once in the console, and deletes the old folder only once it's empty). Your config.yml and your dialogues are never overwritten by an update.
  3. The icon symbols need no pack at all. For the internal RPG panels to serve themselves, you need a reachable public address and a port (8083 by default). A failure to open the port is logged in the console, but a firewall or NAT block may just look like a failed download on the client side. With buttons: auto, players only see panels after successfully downloading the pack; if it fails, they simply see the plain-text presentation.
  4. Every time you edit a dialogue, run /ud reload. The console tells you which file, which node, and which reference is broken if something fails. /ud reload doesn't close conversations already in progress: close and reopen them after reloading their definition. Changing the port or the pack delivery does require a server restart; reloading does not restart the pack's own server.

Tip: if another plugin (EcoItems, for example) already delivers a merged resource pack to you, you don't need UltraDialogue to serve its own separately. The Configuration section below explains how to merge the plugin's ZIP into that external pack and switch to buttons: pack.

Let's build a character from scratch: a lookout who greets you differently the first time, and offers to open a shop or tell a secret.

Step 1 — create the file. Copy dialogues/example.yml (or start from scratch) as dialogues/vigia.yml. The file name, without the extension, is the dialogue's id: vigia.

Step 2 — write the header. name is what's shown as the screen's heading; npcs are the FancyNpcs NPC names that should open this dialogue on click (it can be left empty if you're going to use it with an entity tag or with /ud open); portrait is the image next to the text, with alternatives separated by |.

Step 3 — decide where it starts. start is a list of rules; the first one whose if is true is used. Here we use a flag to tell a first visit apart.

Step 4 — write the nodes. Each node is a turn: its text (what the character says) and its answers (the player's buttons).

name: '&b&lThe Lookout'
npcs: [vigia]
portrait: 'npc | item:SPYGLASS'
sound: 'entity.villager.ambient 0.6 1.1'

start:
  - if: '!flag:vigia_visto'
    node: primera_vez
  - node: menu

nodes:
  primera_vez:
    on-show: ['flag: vigia_visto']
    text:
      - 'Far enough. Not many make it up this tower.'
      - 'As long as you cause no trouble, you are welcome here.'
    next: menu                 # no "answers": a single "Continue" button

  menu:
    text: 'What do you need?'
    answers:
      - text: '&2See the shop'
        icon: accept
        actions: ['menu: shop']
      - text: '&7What can you see from up here?'
        tooltip: '&7The lookout knows every path.'
        goto: vista
      - text: '&8Nothing, thanks'
        goto: end

  vista:
    text: 'From this tower I see the whole valley... and anyone approaching through it.'
    answers:
      - text: '&7« Back'
        goto: menu

Step 5 — try it out. You don't need any NPC to open the dialogue directly:

/ud open vigia

Step 6 — reload and link it. If you edit the file while the server is running, use /ud reload. For a real FancyNpcs NPC to open it on click, create the NPC with the name vigia (the internal name, not the display name) — that's it, no need to touch npcs.yml. If you're using another entity (a villager, say), tag it instead of using FancyNpcs: /tag @e[type=villager,limit=1,sort=nearest] add dialogue:vigia.

dialogues/example.yml ships with, commented out, practically every option that exists — it's the best starting point to copy and paste from.

Dialogue file format

Every file accepts its keys in English or in Spanish, interchangeably (so files from the earlier private plugin, "Dialogos", load without needing to be touched). The table below lists each key alongside its Spanish alias.

English Spanish alias Where it's used
name nombre dialogue header
portrait retrato header
sound sonido header and node
columns columnas header and node
start inicio header
node nodo inside a start rule
if si conditions (start rule, reply, variant)
nodes nodos header
speaker (also name/nombre) — node: who speaks on that turn
text texto node and variant
variants variantes node
on-show al-mostrar node
next siguiente node
show mostrar node
title titulo / título node
answers respuestas node
nav navegacion / navegación node
layout filas node
tooltip — reply
goto ir reply
actions acciones reply, on-show
always siempre reply
icon icono reply
size tamano / tamaño reply
width ancho reply
locked bloqueada / bloqueado reply
style estilo reply, node, dialogue
border borde reply, node, dialogue
border-color color-borde reply, node, dialogue
fill-color color-fondo reply, node, dialogue
affinity afinidad header (its own section) and action

Note: the plugin's code is entirely in English (identifiers, comments, console messages); bilingual support is only for the keys in the dialogue files.

File header

name: '&2&lThe Wandering Merchant'   # title shown above the screen
npcs: [merchant]                     # FancyNpcs NPC names that open this dialogue
portrait: 'npc | item:EMERALD'       # alternatives separated by "|": the first one that can be shown wins
sound: 'entity.villager.ambient 0.7 1'   # optional; if missing, uses talk-sound from config.yml
columns: 1                           # default buttons per row for this whole dialogue (optional)

The portrait (portrait) accepts, separated by | as alternatives:

Value What it shows
npc the skin of the NPC or entity that opened the chat, as a 2D face
player:Name (jugador:) that player's skin
texture:<base64> (textura:) a fixed texture, as a 2D face
item:MATERIAL the item to the left of the text (shows no face)
none nothing

start: where the conversation begins

A list of rules; the first one whose if is true wins. A bare start: node_name (without a list) also works, for dialogues that always start the same way.

start:
  - if: 'affinity >= 25'
    node: friend
  - if: '!flag:conocido'
    node: first_time
  - node: normal

Nodes (nodes)

Each node is a conversation turn. Its fields:

Field What it does
speaker who speaks on this turn (otherwise, the dialogue's name)
text one line, or a list of lines, that the character says
variants instead of text, several versions of the line; one is picked at random each time, never the same one twice in a row. Each variant can have its own if
on-show actions that run as soon as the node is shown (set a flag, give an item, play a sound...)
answers the list of the player's reply buttons
next if the node has no answers, a single "Continue" button leads to this node
show if greater than 0, only that many replies not marked always are shown, chosen at random each time
columns buttons per row, for this node only
title screen heading for this node only (otherwise, the character's name)
nav navigation buttons (Back, Exit, or a full reply) on the last row
layout buttons per row, row by row — see Free rows

Replies (answers)

answers:
  - text: '&2Show me your goods'
    icon: accept
    size: 2
    tooltip: '&7Opens the "shop" menu'
    if: 'permission:group.vip'
    locked: '&7VIP only.'
    actions: ['menu: shop']
    goto: fin_amable
Field What it does
text the button's text (can be left empty if it's icon-only, with size: 1)
tooltip text shown when hovering over it
if condition(s) for the reply to be shown — see Conditions
locked if the if fails, instead of hiding the reply it's shown grayed out with this notice (locked: true uses the language file's default text)
actions list of actions that run on click — see Actions
goto which node it leads to after the actions (end or fin ends the chat)
always doesn't count toward show's rotation — always appears
icon name of one or more icons (icon: quest side)
size / width the button's size — see Buttons
style / border / border-color / fill-color the RPG panel appearance for this reply, if force/force-colors allow it
nav:
  - back                              # previous screen of this conversation
  - text: '&eReward'                  # any full reply also works inside nav
    actions: ['console: give %player% emerald 1']
  - exit                              # fixed at the bottom of the screen
  • back walks back through this conversation's screen history, even across different dialogues. On the first screen, it closes the chat (it's always shown, so the grid's shape never breaks).
  • exit goes in the footer of the screen. Minecraft only has room for one fixed button there; back and the rest of nav take up the grid's last row.
  • Without nav:, a "Goodbye" button appears at the end.

Buttons, sizes and icons

Sizes

Size Width Pause-menu equivalent
size: 1 20 px the small square button ("report")
size: 2 98 px "Options...", half width
size: 3 150 px a medium button
size: 4 204 px "Disconnect", full width
width: 130 any, 1–1024 px exact width

Native buttons are 20 px tall; free-row panels (with the pack) are 24 px. columns: controls the native grid, for the whole dialogue or a single node; layout: controls the free rows.

Icons

/ud icons lists them in-game, drawn exactly as that player sees them. Since 1.1.2 they're text symbols, so they look the same with or without a resource pack:

Name English Symbol Meaning
mision quest ! (orange) there's a quest
principal main • (green) marker: main quest
secundaria side • (blue) marker: side quest
acertijo riddle • (purple) marker: riddle
aceptar accept ✓ (green) accept
volver back « back
salir exit × (red) exit
premio reward ± (yellow) quest rewards
cuento story ♪ (light blue) a story the NPC tells
probabilidad chance % something may happen (or drop) by chance

Several icons on the same button, separated by spaces: icon: quest side shows ! • with the blue marker. [quest, side] and quest+side also work.

Important: 1.1.2 replaced the 20 pixel-art icons from 1.1.1 (heart, gem, gift, star, shop...). An icon that no longer exists breaks nothing: the button is shown without it, and the console warns once per dialogue. If you're coming from 1.1.1, check your icon-only replies (no text): they would now end up with a blank button.

Free rows (layout) and RPG panels

Minecraft only lets servers arrange real buttons in a uniform grid, with a single exception: one leftover button at the end can take its own row. That's enough for a "2 + 2 + 1 wide" layout, but not for "1 wide + 2 + 2", nor for three small buttons side by side in one row (Back · Exit · Reward).

layout: [1, 2, 2, 1] solves that by drawing the buttons as clickable text inside the dialogue's body, instead of as real buttons:

nodes:
  menu:
    title: '&6Quests from Brann'
    text: 'What do you want to ask?'
    layout: [1, 2, 2, 1]
    answers:
      - {text: '&fMain quest', icon: quest main, goto: principal}
      - {text: '&fSide quest', icon: quest side, goto: q1}
      - {text: '&fSide quest', icon: quest side, goto: q2}
      - {text: '&fRiddle', icon: quest riddle, if: 'affinity >= 10', locked: '&7Not yet.', goto: q3}
      - {text: '&fA story', icon: story, goto: q4}
      - {text: '&7Just passing by', goto: end}
    nav:                              # REAL buttons, all in one row, in this order
      - back
      - exit
      - {text: '&eReward', icon: reward, actions: ['console: give %player% emerald 1'], goto: menu}

Free-row layouts with buttons in different row shapes

Real screens from a server configured with layout:: each row has its own number of buttons, something Minecraft's native grid can't do on its own.

  • Rows are filled with the replies in order; any leftovers get one per row.
  • nav: buttons are real Minecraft buttons, all in one row at the bottom, in the order they're written — so Back, Exit and a "Reward" can end up side by side.
  • Without the pack, each layout: button looks like [ text ], formatted by screen.layout.button-format in config.yml. With the pack loaded (and buttons: auto or pack), it's drawn as a panel: a flat dark background and a double frame, colored by the last colored icon the button has.
  • Limitation: layout: panels don't light up on hover (only the text line responds to clicks); the nav: row does, because it's native.

Quest board with colored frames per task type

Each panel's frame follows the color of its last icon: orange for quest, green for main, blue for side, purple for riddle.

The pack that draws these panels has been very lightweight since 1.1.3: it only includes the flat background and the double border (there used to be ten different backgrounds and ten different borders). Older style or border names in a dialogue file or in config.yml still load without error: they fall back to flat/double with a one-time console warning per name.

Replies and branches

The simplest way to branch is by chaining goto: each reply leads to another node, and that node has its own replies. end (or fin) as a destination ends the chat.

To keep the same conversation from feeling identical every time:

  • variants: gives several versions of a line; one is picked at random each time the node is shown, never the same one twice in a row. Each variant can have its own if.
  • show: N shows only N of the node's replies (out of the ones without always: true), chosen at random on each visit. Handy so an NPC doesn't always drop the same five questions in the same order.
  • always: true takes a reply out of that rotation: it always appears (typically the farewell).
stranger:
  variants:
    - 'Yes? What do you want?'
    - 'I do not usually talk to strangers.'
  show: 2
  answers:
    - text: 'Hello, who are you?'
      goto: quien
    - text: 'Where are you from?'
      goto: origen
    - text: 'Nice compass.'
      goto: compas
    - text: 'Goodbye.'
      always: true
      goto: end

Affinity

Every player has a hidden affinity number with each character, starting at 0. Replies change it and conditions read it — so the same NPC can be cold with strangers and open up with friends:

start:
  - if: 'affinity >= 25'
    node: friend
  - node: stranger

nodes:
  stranger:
    answers:
      - text: 'Hello. Who are you?'
        actions: ['affinity: +2']
        goto: quien
      - text: 'Get out of my way.'
        actions: ['affinity: -3']
        goto: end
  • The player never sees the number or finds out it changed. Staff can check it with /ud affinity <player> <id>.
  • Anti-farming: a character only gives affinity once every cooldown-minutes (30 by default), and at most max-per-talk points (3) per conversation. Losing affinity has no limit.
  • The defaults live in config.yml, under affinity:; each dialogue can bring its own affinity: section to override them just for that character.
  • It only exists in dialogues that use it: a menu NPC that never mentions affinity behaves exactly as it did before this feature existed.
  • A variant can have its own if ({text: '...', if: 'affinity >= 10'}).

dialogues/kadir.yml, bundled with the plugin, is a full example with four friendship levels, rotating questions, and a one-time gift.

Conditions

Used in if: — of a start rule, a reply, a variant, or locked:. Every line in the list must be true (they're an AND); within a single line, || means "or".

if:
  - 'permission:group.vip || permission:ultradialogue.admin'
  - '!flag:ya_hablado'
Condition Spanish alias True when
permission:<node> permiso: the player has that permission
flag:<name> marca: the player has that flag
has: ITEM N tiene: the player carries at least N of that item, anywhere in the inventory
hand: ITEM N mano: they're holding at least N of that item in the main hand
quest: completed <id> mision: completada / terminada they finished that BeautyQuests quest
quest: started <id> mision: en-curso / empezada they started it and haven't finished it
quest: active <key> mision: activa they're currently on a ULTRADIALOGUE stage with that key
affinity >= N afinidad their affinity with this character meets the comparison
affinity:<id> >= N afinidad:<id> their affinity with another character meets the comparison
%placeholder% >= N — any PlaceholderAPI placeholder, compared

Any condition can be negated with ! at the start (!flag:conocido), unless it starts with !=, which is the "not equal to" operator.

Placeholder comparisons accept ==, !=, >=, <=, >, < and contains (contiene):

if: '%vault_eco_balance% >= 1000'
if: '%player_name% contains Gupe'

Note: the numeric comparison understands both 1.234,50 and $1,234.50 — the two most common formats economy plugins give their placeholders in.

Without BeautyQuests installed, the whole plugin still loads normally: quest conditions are simply always false, and the console warns about it once per dialogue.

Reply grid with entries locked and grayed out

Replies with locked: show dimmed while their condition isn't met; clicking one only shows the notice in the action bar, without running anything.

Actions

Used in actions: (of a reply or of on-show). They run in order, top to bottom.

Action Alias What it does
goto: node ir: goes to another node (end ends the chat)
dialogue: id [node] dialogo: / dialog: switches to another dialogue, optionally at a specific node
menu: name — opens a menu (runs menu-command from config.yml; DeluxeMenus by default)
command: spawn comando: runs the command as the player
console: give %player% diamond 1 consola: runs it as console
message: text mensaje: private message to the player
broadcast: text anuncio: message to the whole server
actionbar: text — text in the action bar
title: Title\|Subtitle titulo: on-screen title
sound: entity.villager.yes 1 1 sonido: also accepts ENTITY_VILLAGER_YES
flag: name marcar: sets a flag on the player
unflag: name desmarcar: removes it
affinity: +2 / -3 / =10 afinidad: changes affinity (with another character: affinity: otro_id +2)
take: ITEM N quitar: takes N of that item (all or nothing)
give: ITEM N dar: gives N of that item
quest: complete <key> mision: completar completes every active ULTRADIALOGUE stage with that key for the player
quest: start <id> mision: empezar starts the quest, respecting its requirements
quest: force <id> mision: forzar starts it with no requirements
close cerrar: closes the screen
actions:
  - 'message: &6You are VIP!'
  - 'sound: ENTITY_PLAYER_LEVELUP'

Important: when a reply ends the chat (opens a menu, runs a command...), the screen closes before the actions run, so the menu that opens ends up on top.

Deliveries: items, shop and rewards

The has:/hand: conditions and the take:/give: actions recognize several item formats, each one with a number at the end (1 by default):

Format What it matches
IRON_INGOT / minecraft:iron_ingot that vanilla material — plain items only: one with a hidden tag or a custom model from another plugin doesn't count, so a vanilla item never confuses an UltraBoss trophy with a regular ingot
ultraboss:<id> an UltraBoss item (also under its older names)
ultrarevive:self / boost / end an UltraRevive totem
pdc:<namespace:key>=<value> any plugin's hidden tag
item_model:<namespace:model> by its item model
name:&6Cellar Key (nombre:) by its display name (last resort)
deliver:
  text: 'Did you bring the iron for the forge?'
  answers:
    - text: '&aDeliver 16 iron ingots'
      icon: accept
      if: 'has: IRON_INGOT 16'
      actions:
        - 'take: IRON_INGOT 16'
        - 'give: EMERALD 4'
        - 'affinity: +3'
      goto: gracias
    - text: '&8I do not have them yet'
      if: '!has: IRON_INGOT 16'
      goto: end
  • take: is atomic: it counts first and takes nothing if any is missing. Main hand first, then the rest of the inventory.
  • If take: fails, the rest of that reply's actions are cut short — a reward is never given without being paid for first. The player gets the item-missing message ("You don't have what's needed anymore.").
  • Conditions are re-checked on click. If you open the dialogue with 16 ingots, drop them, and then press "Deliver", nothing is taken or given: the screen simply redraws itself.
  • give: builds vanilla items on its own; UltraBoss and UltraRevive items are delivered with each plugin's own command, so they keep all their tags, name and texture.
  • A start rule can react to what the player is holding: if: 'hand: IRON_INGOT 16'.

Quests and BeautyQuests

start:
  - if: '!quest: completed 12'
    node: too_soon
  - node: final_scene

UltraDialogue registers its own stage type in BeautyQuests, ULTRADIALOGUE, so a dialogue NPC can be a native part of a quest:

# in the BeautyQuests quest file
'3':
  stageType: ULTRADIALOGUE
  key: brann_hierro
  npc: brann                  # optional, only for the description
  customText: '§7Bring §f16 iron ingots §7to §fBrann'
# in the dialogue
- text: '&aDeliver the iron'
  icon: accept
  if: ['quest: active brann_hierro', 'has: IRON_INGOT 16']
  actions: ['take: IRON_INGOT 16', 'quest: complete brann_hierro']

It can also be created from BeautyQuests' in-game editor (it asks for the key). Two quests sharing the same key advance together.

quest: complete fires a normal Bukkit event, DialogueSignalEvent(player, key), which any plugin can listen for, with or without BeautyQuests installed.

Assigning dialogues to NPCs and opening them

There are four ways a conversation can start:

  1. FancyNpcs. Put the NPC's internal name (the one used in /npc create, not its display name) in the dialogue's npcs: list. Right-clicking (or left-clicking, if left-click: true in config.yml) opens the conversation instead of FancyNpcs' own actions. npcs.yml is never modified: removing the NPC from that list returns it to its normal behavior.
  2. Any entity, with a tag. /tag @e[type=villager,limit=1,sort=nearest] add dialogue:merchant (the older dialogo: tag also works). Useful for villagers, armor stands, or any entity that doesn't come from FancyNpcs.
  3. The /ud open command. Works from the console or as another plugin's action, and doesn't require any NPC nearby.
  4. As another dialogue's action, with dialogue: id [node].

When opened by clicking an NPC or a tagged entity, there's also a cooldown (cooldown-ms in config.yml, 700 ms by default) and a maximum distance (max-distance, 8 blocks) beyond which replies stop working — so nobody can walk away mid-conversation and keep pressing buttons from afar.

The button editor (/ud)

Since 1.1.3, /ud with no arguments (or explicitly /ud editor) opens an in-game menu for admins: pick a dialogue, pick one of its nodes, and see its buttons — both the main replies and the nav: row.

The editor only touches buttons. The NPC's own lines, its conditions, its actions and affinity are still edited in the dialogue's .yml file; the menu's help text reminds you of that. "Save" rewrites only that node's answers:, nav: and layout: lines — every other node, every other key, and the rest of the file's comments stay untouched.

For each button you can:

  • Edit its text — the menu closes, you type the new line in chat (or cancel to leave it as is), and the menu reopens.
  • Change its icon — one of the 10 approved presets, or none.
  • Change its size — cycles through auto / 1 / 2 / 3 / 4 (right-click to go backward).
  • Move it up or down, or, in a node with layout:, move it to another row.
  • Delete it, with a confirmation step.
  • Add a new one — by picking an existing button as a template (its goto, its actions and its conditions are copied as is), then editing its text and icon.

"Test" opens the dialogue exactly as it's currently saved; "Save" writes the buttons to the file; "Cancel", or simply closing the menu, discards any unsaved changes. The permission is the same ultradialogue.admin as any other /ud subcommand.

Category menus with colored buttons and submenu hierarchy

Each category uses its own frame color; "Back" and "Exit" always stay as native buttons at the foot of the screen.

Commands

All admin subcommands require the ultradialogue.admin permission (op by default). Command aliases: /ud, /dialogue, /dialogo. The legacy dialogos.admin permission also grants it.

Command Spanish alias What it does
/ud or /ud editor — opens the button editor (in-game only)
/ud open <id> [player] [node] /ud abrir opens a dialogue manually — works from the console and from other plugins
/ud reload /ud recargar reloads the config, the language and all dialogues
/ud list /ud lista loaded dialogues and their NPCs
/ud flags <player> [clear [flag]] /ud marcas ... [borrar [marca]] view or clear a player's flags
/ud affinity <player> [id] [set\|add\|reset] [n] /ud afinidad view or change affinity (staff only; add ignores the anti-farming limits)
/ud icons /ud iconos every icon with its name, drawn exactly as that player sees it

Players don't need any permission to talk to the NPCs.

Permissions

Permission Default Grants
ultradialogue.admin op opening, reloading and managing dialogues, including the button editor
dialogos.admin false (inherits ultradialogue.admin) compatibility node for the plugin's former name ("Dialogos")

Configuration

The full config.yml, with its real default values:

# en | es — language of the plugin's messages
language: en

# dialog = the native dialogue screen (Minecraft 1.21.6+)
# chat   = text with clickable replies in chat
# Clients older than 1.21.6 (coming in through ViaVersion) always get the chat version.
mode: dialog

# legacy = &a &l &#ff8800   |   minimessage = <green> <bold> <#ff8800>
format: legacy

# Console command that runs the "menu: <name>" action. Opens a DeluxeMenus menu by default.
menu-command: 'dm open {menu} {player}'

screen:
  text-width: 260
  button-width: 220
  columns: 1
  close-with-esc: true
  exit-button: false       # adds a "Goodbye" button below each screen's replies
  nav-width: 64             # width of the nav buttons (back, exit, reward...)
  locked-color: '&8'        # color of a "locked" reply while its "if" isn't met
  locked-sound: 'block.note_block.bass 0.6 0.8'
  layout:
    button-format: '&8[ {text}&8 ]'     # "{text}" = the reply's text
    locked-format: '&8[ {text}&8 ]'
    gap: 2                              # spaces between two buttons on the same row
    row-width: full                     # full = every row the same width · fit = each row sized to its text · a number = minimum width in pixels
    buttons: auto                       # auto = panels drawn only for those who loaded the pack · pack = always drawn · text = always "[ text ]"
    look: filled
    style: liso
    border: doble
    force: true              # true = applies this style/border to EVERY dialogue, ignoring their own
    force-colors: true        # true = uses the semantic colors, ignoring each dialogue's own colors
    force-border: false       # true = applies the "border" above to every dialogue, but each one keeps its own background
    border-colors:
      default: '#FFFFFF'      # questions (no icon)
      mision: '#F2B21E'
      principal: '#4CD964'
      secundaria: '#4F8BFF'
      acertijo: '#B36BFF'
      aceptar: '#4CD964'
      premio: '#FFD23F'
      cuento: '#7FD6FF'
      probabilidad: '#FF9F43'
      volver: '#C6E86B'
      salir: '#E04B4B'
      locked: '#5A5A5A'

left-click: false           # only right-click opens a dialogue; true = left-click too
cooldown-ms: 700             # milliseconds between two clicks on the same NPC
max-distance: 8.0            # if the player walks farther than this from the NPC, replies stop working
talk-sound: 'entity.villager.ambient 0.6 1.1'   # sound on each line the NPC says; each dialogue can override it

affinity:
  min: 0
  max: 100
  cooldown-minutes: 30       # a character only GIVES affinity once every so many minutes per player (anti-farming)
  max-per-talk: 3            # the most affinity a single conversation can give

icons:
  mode: auto                 # auto = images for those who loaded the pack, text symbols for everyone else · sprite = always images · text = always symbols

resource-pack:
  enabled: true
  mode: internal              # internal | url
  host: "AUTO"                # AUTO looks up the server's public IP via api.ipify.org
  port: 8083
  url: ""
  force: false
  prompt: ""                  # empty = uses the language file's text

Tip: if another external provider (EcoItems, for example) delivers the merged resource pack, disable UltraDialogue's own delivery and assume the pack already includes the font:

resource-pack:
  enabled: false
screen:
  layout:
    buttons: pack

Merge these keys into your existing config.yml; don't replace the whole file or duplicate the screen: section. buttons: pack assumes the font is already present and can't confirm an external provider's state. For the plugin's mode: url, the external provider must serve exactly the same bytes as the pack bundled in the jar: its hash is computed from the embedded ZIP, not from a pack merged externally.

Placeholders and integrations

PlaceholderAPI

Placeholder Value
%ultradialogue_affinity_<id>% the player's affinity with that character
%ultradialogue_flag_<flag>% true / false

These work as real requirements in BeautyQuests (placeholderRequired), to lock menu items in DeluxeMenus, or in any other plugin that reads placeholders.

FancyNpcs

Link a dialogue to an NPC by listing it in npcs:; clicking opens the conversation instead of the NPC's own actions. npcs.yml is never touched.

DeluxeMenus (or any menu plugin)

The menu: name action runs the menu-command from config.yml (by default, dm open {menu} {player}, for DeluxeMenus). Changing that line makes it work with any other menu plugin that accepts opening a menu via a console command.

ViaVersion

Clients on a version older than 1.21.6 get the same conversation as text in chat, with clickable replies, with no extra work needed from the server operator.

BeautyQuests 2.1

Quest conditions (quest:) and actions (quest:), and its own ULTRADIALOGUE stage type — see Deliveries: items, shop and rewards.

Quest confirmations with their requirements and rewards visible

Before accepting a job, the player sees what it's about, who's asking, the steps, and the reward — all built with layout: and inventory conditions.

Bedrock players

Behavior on Bedrock/Geyser is not verified. The only confirmed and documented fallback is for Java clients older than 1.21.6 via ViaVersion, which get the conversation in chat instead of the native screen. If you run a cross-play server, test the behavior in your own setup before assuming anything, and consider forcing mode: chat if you need a uniform presentation for every player.

Full examples

A wandering merchant

Based on dialogues/merchant.yml, bundled with the plugin: greets you differently the first time and opens a DeluxeMenus menu.

name: '&2&lThe Wandering Merchant'
npcs: [mercader]
portrait: 'npc | item:EMERALD'
sound: 'entity.wandering_trader.ambient 0.7 1'
start:
  - if: '!flag:conocio_mercader'
    node: intro
  - node: hola
nodes:
  intro:
    on-show: ['flag: conocio_mercader']
    text:
      - 'Psst! You, with the empty pockets. Come closer.'
      - 'I have things you will not find anywhere else. But I never stay long...'
    next: hola
  hola:
    text: 'My wares change every &fMonday&f. What you see today might not be here tomorrow.'
    answers:
      - text: '&2Show me your goods'
        tooltip: '&7Opens the "shop" menu (DeluxeMenus).'
        actions: ['menu: shop']
      - text: '&7Where do you get all this?'
        goto: secreto
      - text: '&8Not interested'
        goto: end
  secreto:
    text: 'A good merchant never reveals his suppliers. &7&o*smiles*'
    answers:
      - text: '&2Show me your goods'
        actions: ['menu: shop']
      - text: '&8Goodbye'
        goto: end

A delivery job with a quest

A blacksmith who asks for materials, conditioned on an active BeautyQuests stage, and who pays with affinity and a reward.

name: '&6&lBrann the Blacksmith'
npcs: [brann]
portrait: 'npc | item:ANVIL'
start:
  - node: taller
nodes:
  taller:
    text: 'The forge has been cold for three days. I am short on iron.'
    layout: [1, 1]
    answers:
      - text: '&aDeliver 16 iron ingots'
        icon: accept
        if: ['quest: active brann_hierro', 'has: IRON_INGOT 16']
        locked: '&7I do not have enough iron, or I do not need it anymore.'
        actions:
          - 'take: IRON_INGOT 16'
          - 'give: EMERALD 4'
          - 'affinity: +3'
          - 'quest: complete brann_hierro'
        goto: gracias
      - text: '&7What exactly do you need?'
        icon: story
        goto: detalles
    nav:
      - back
      - exit
  detalles:
    text: 'Sixteen iron ingots. No more, no less.'
    answers:
      - text: '&7« Back'
        goto: taller
  gracias:
    text: 'Finally! With this the forge is running again. Thank you, truly.'
# in the corresponding BeautyQuests quest
'1':
  stageType: ULTRADIALOGUE
  key: brann_hierro
  npc: brann
  customText: '§7Bring §f16 iron ingots §7to §fBrann'

A guide with growing affinity

Based on dialogues/kadir.yml, bundled with the plugin: a traveler who opens up based on how much you've talked to him, with rotating questions and a one-time gift.

name: '&6&lKadir'
npcs: [kadir]
portrait: 'npc | item:COMPASS'
affinity:
  min: 0
  max: 100
  cooldown-minutes: 30
  max-per-talk: 3
start:
  - if: 'affinity >= 50'
    node: amigo_cercano
  - if: 'affinity >= 25'
    node: amigo
  - if: 'affinity >= 10'
    node: conocido
  - node: desconocido
nodes:
  desconocido:
    variants:
      - '&7&o*looks at you* &fYes? What do you want?'
      - 'I do not usually talk to strangers. Make it quick.'
    show: 2
    answers:
      - text: '&7Hello. Who are you?'
        actions: ['affinity: +2']
        goto: quien
      - text: '&7Nice compass.'
        actions: ['affinity: +1']
        goto: compas
      - text: '&8Get out of my way, old man.'
        actions: ['affinity: -3']
        goto: ofendido
      - text: '&8Goodbye.'
        always: true
        goto: end
  quien:
    text: 'Kadir. Cartographer, traveler... whatever it takes to keep walking.'
    answers:
      - text: '&7« Keep talking'
        actions: ['dialogue: kadir']
  compas:
    text: '&7&o*puts it away* &fIt does not point north. It points to what I am looking for. None of your business.'
    answers:
      - text: '&7« Keep talking'
        actions: ['dialogue: kadir']
  ofendido:
    text: '&7&o*frowns* &fManners cost nothing, you know?'
  amigo_cercano:
    answers:
      - text: '&dKadir, got something for me?'
        if: '!flag:regalo_kadir'
        goto: regalo
      - text: '&8See you around, friend.'
        always: true
        goto: end
  regalo:
    text: 'Here. It has been with me on every trip. Now it is your turn to look after it.'
    on-show:
      - 'flag: regalo_kadir'
      - 'console: give %player% minecraft:recovery_compass 1'
      - 'sound: entity.player.levelup 0.8 1.2'
    answers:
      - text: '&7Thank you, Kadir. I will take care of it.'
        goto: end

Multi-page menu with the Back button following the conversation's history

Back remembers the path taken within the same conversation, even if it crossed from one dialogue into another.

FAQ

Do I need a resource pack to use UltraDialogue? No, not for the basic presentation. Buttons and icons are plain text by default. The resource pack is only needed if you want the RPG panels with a background and frame from layout:; without it, those same buttons look like [ text ].

Does it work without FancyNpcs? Yes. You can open a dialogue with /ud open, from another plugin, or by tagging any entity with dialogue:<id>. FancyNpcs just adds the convenience of linking it by click.

What happens if I don't have BeautyQuests installed? The plugin still loads. Quest conditions (quest:) are always false, the quest: start/force actions do nothing, and the console warns about it once per dialogue. quest: complete still fires the DialogueSignalEvent event even if BeautyQuests isn't there.

How do I stop an NPC from always repeating the same greeting? With a flag (flag:) set in on-show the first time, and a start rule that checks it; or with variants:, which picks a different line each time.

Can I have different dialogue depending on how well I get along with the character? Yes, that's exactly what affinity is for: start rules with if: 'affinity >= N', and replies visible only from a certain level up.

If I edit a dialogue while players are talking to that NPC, does anything break? Nothing breaks, but it doesn't hot-update either: /ud reload doesn't close conversations already open. Ask those players to close and reopen the chat after reloading.

Does it support Folia? No. plugin.yml declares it explicitly (folia-supported: false).

And Bedrock/Geyser? Not verified. The only tested fallback is for old Java clients via ViaVersion.

Can I use another menu plugin instead of DeluxeMenus? Yes, any plugin that supports opening a menu via a console command: just change menu-command in config.yml.

Where are flags and affinity stored? On the player itself (PersistentDataContainer), not in a separate database. There's no file to back up or sync between servers, unless you use something like a shared-profiles network plugin.

What happens if give: can't build the item I'm asking for? It logs a warning in the console (Cannot give '...') and gives nothing; check that the format is a valid material, ultraboss:<id> or ultrarevive:<id> — the pdc:, item_model: and name: formats are good for identifying an existing item, but not for creating one from scratch.

Version history

1.1.3 — 2026-10-15

A button editor in /ud, a much smaller resource pack, and reorganized code. It doesn't change how dialogue files are written or how conversations play out.

  • In-game button editor: /ud (or /ud editor) opens a menu to pick a dialogue, a node, and edit each button — text, icon, size and position — without touching a text editor. It only touches buttons; the rest of the node is edited in the file.
  • Much smaller resource pack: only includes the flat background and the double border; the jar went from ~570 KB down to ~200 KB. Older style or border names still load without error, with a one-time console warning.
  • Its own data folder: plugins/UltraDialogue/ instead of the shared plugins/UltraView/UltraDialogue/. Migration is automatic and announced once.
  • Reorganized code into packages (command, config, migration, model, registry, gui, listener, pack, hook, util) and fully translated into English (dialogue files still accept Spanish keys, as always). Built with Gradle (Kotlin DSL) in addition to compilar.sh.

1.1.2

Free rows, a clean double-frame theme, quest symbols, and better navigation. Server target: Paper 26.1.2 with Java 25.

  • layout: / filas: per node, for layouts like [1, 2, 2, 1] or [3, 1]; rows of 1 to 6 buttons, drawn as clickable text in the dialogue's body. nav: becomes a row of real buttons.
  • Simple RPG panels: flat dark background and double frame by default (style: liso, border: doble, look: filled); the frame color follows each button's icon. border-colors lets you customize the palette.
  • Ten text icon presets replace the 20 pixel-art sprites from 1.1.1 (heart, gem, gift, star, shop...), which no longer have any effect.
  • locked:, Back with history across dialogues, a fixed Exit at the bottom, and per-node title:.

1.1.1 — 2026-09-26

  • Button sizes size: 1 to 4 (20/98/150/204 px) or an exact width: N; columns: per node; a reply can be icon-only, with no text.
  • 20 pixel-art icons (replaced in 1.1.2), served by the plugin on port 8083, with a text fallback for anyone who didn't load the pack.
  • 2D NPC face next to its name, instead of the dark-silhouette 3D head from before.
  • Deliveries: has:/hand: conditions and take:/give: actions; take: is all-or-nothing, re-checked on click.
  • Quests and story gating: quest: completed/started/active conditions, quest: complete/start/force actions, BeautyQuests' ULTRADIALOGUE stage type (optional), and the DialogueSignalEvent event.
  • Placeholders %ultradialogue_affinity_<id>% and %ultradialogue_flag_<flag>%.
  • Spanish keys: icono, tamaño/tamano, ancho, columnas, tiene, mano, quitar, dar, mision (completada, en-curso, activa, completar, empezar, forzar).

1.1 — 2026-09-26

  • Affinity: a hidden friendship level per player and per character, with anti-farming (a 30-minute cooldown and a maximum of 3 points per chat, both configurable), manageable with /ud affinity.
  • variants: and show: N for conversations that don't always repeat the same way; always: true to take a reply out of that rotation.
  • New example kadir.yml. Spanish keys: afinidad, variantes, mostrar, siempre, cooldown-minutos, max-por-charla.

1.0 — 2026-09-25

First public release, for Minecraft 26.1.2. Started out as a private plugin called "Dialogos", built for a single server.

  • Native dialogue screens (1.21.6+): portrait, text, and replies as buttons. No resource pack, no client mod.
  • Branching conversations in YAML: nodes, replies, next, start rules.
  • 13 actions: goto, dialogue, menu, command, console, message, broadcast, actionbar, title, sound, flag, unflag, close.
  • Permission, flag and PlaceholderAPI-comparison conditions.
  • Per-player flags (PersistentDataContainer), portraits with alternatives, FancyNpcs support, entity tags, chat fallback for older clients (ViaVersion), English and Spanish.
  • Changes from the private plugin: renamed from "Dialogos" to UltraDialogue (/ud command, ultradialogue.admin permission, mc.gupe.ultradialogue package); automatic migration from plugins/Dialogos/dialogos/ the first time it starts.

Links