For mod developers
Depending on RPG Dialogue, opening conversations from code, registering your own actions and conditions, and every hook it offers.
Everything on the other pages is a datapack file and needs no Java. This page is for a mod that wants to add to the system rather than write for it.
Depending on it
The jar is on Modrinth, which serves a public Maven. There is no repository of ours to add:
repositories {
maven { url = "https://api.modrinth.com/maven" }
}
dependencies {
// Fabric
modImplementation "maven.modrinth:rpg-dialogue:<version>"
// NeoForge
implementation "maven.modrinth:rpg-dialogue:<version>"
}Source is not published; the javadoc jar is
The javadoc documents the whole API. The licence grants you the right to depend on it and to redistribute the unmodified jar in a modpack, and you may license your own mod however you like. Anything you write for it, dialogues, murmurs and translations, is yours outright.
Opening a conversation
DialogueManager.open(player, ResourceLocation.fromNamespaceAndPath("yourmod", "greeting"), speaker);That is the whole of it. speaker may be null for a conversation with nobody in particular, in which case conditions that ask about a speaker fall back to the reader.
Also useful:
| Call | Answers |
|---|---|
DialogueManager.has(id) | Whether any pack defines it. |
DialogueManager.hasCompleted(player, id) | Whether they have read it through. Persisted, and carried across death. |
DialogueManager.isTalkingTo(player, entity) | Whether those two are mid-conversation. |
DialogueManager.speakingTo(entity) | Who, if anybody, that entity is talking to. |
DialogueManager.interrupt(entity) | Ends it, for when your mob has somewhere more urgent to be. |
Adding an action, condition or value
A record, a MapCodec, and one call. Register while your mod initialises, before any pack is read.
public record RingTheBell(int radius) implements DialogueAction {
public static final MapCodec<RingTheBell> CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group(Codec.INT.optionalFieldOf("radius", 16).forGetter(RingTheBell::radius))
.apply(instance, RingTheBell::new));
@Override public MapCodec<? extends DialogueAction> codec() { return CODEC; }
@Override public void run(DialogueContext context) {
// context.player(), context.target(), context.speaker()
}
}
DialogueAction.register(ResourceLocation.fromNamespaceAndPath("yourmod", "ring_the_bell"),
RingTheBell.CODEC);Packs then write { "type": "yourmod:ring_the_bell", "radius": 24 }.
DialogueCondition.register and DialogueValue.register work the same way. Namespace them to your own mod: an unnamespaced name in a file means one of this mod's, so taking one of those over is not something a pack can ask for by accident.
Hooks
| Hook | For |
|---|---|
DialogueEvents.OPENED / CLOSED / ABANDONED | Every conversation ends exactly once, through CLOSED if the player was there and ABANDONED if they were not, so anything held for its duration has somewhere to let go. |
Attention.HOLDING / BROKE_OFF | Say that a mob of yours is attending to somebody, and it will stand still and turn to face them for as long as that holds. Two neighbours mid-conversation are the obvious case. A conversation and a trade already do this. BROKE_OFF tells you when it is over, so you do not have to work out for yourself when a speaker has somewhere more urgent to be. |
DialogueSpeakers.NAMING | Give entities proper names. "Villager" above a line of dialogue undoes most of what the dialogue was for. First registration to answer wins; a name tag always beats all of them. |
SpeakerBindings.route | Choose a conversation in code at the moment of the click, for the one thing a binding cannot do: name a file that depends on the speaker. See Routing. |
Speech.BEGAN / Speech.REVEALED | Client-side. Make your own noise as a speaker's words appear, per line or per letter. See below. |
MurmurManager.speak | Put a line over anybody's head, to everyone in earshot, in their own voice, without a conversation being opened. The primitive murmurs are built on. |
WorldText.CROWDING | Say that your own marker occupies the space over an entity's head, and murmurs there will lift clear of it. |
DialoguePosable | Implement on your entity to arrange it before it is drawn in the portrait window: drop a spawn animation, hide a marker. |
PlayerFlags | Read and write the same flags set_flag and has_flag use. |
Recently.pick | Reuse the no-repeats memory for your own barks, so yours and ours know about each other. |
| Choice slots | Add your choices to a conversation another mod owns, and leave gaps in yours for a sibling mod to fill. No Java at all; it is a file in your own data/. |
FloatingText | Draw your own world-space text the way murmurs are drawn, rather than nearly that way. |
WorldIcon | The same for a sprite rather than a sentence: a quest marker, a status icon. Pair it with WorldText.CROWDING and murmurs will lift clear of it. Two copies of this code is how two mods' marks end up at different heights over one head. |
`DialoguePosable` is called around each draw
It is undone straight afterwards, because the entity in the window is usually the live one still standing out in the world. Set a flag your renderer reads and clear it again. Never write anything they keep counting.
What a speaker sounds like
This mod plays a speaker's own idle noise, once, as each line begins. A villager hums, a pillager grunts, an iron golem creaks, whatever vanilla already has them say when they have nothing to say. It needs no files and is right for every mob, including modded ones.
That is deliberately all it does. A written voice, meaning a blip every few letters and a pitch per profession, is a large thing to carry and means having an opinion about what a villager sounds like. Valor Core has that, and any mod can do the same:
// from your client initialiser: one blip every five letters, per speaker
Speech.BEGAN.register((speaker, line) -> letters.put(speaker.getId(), 0));
Speech.REVEALED.register((speaker, letter, at) -> {
if (!Character.isLetterOrDigit(letter)) return;
if ((letters.merge(speaker.getId(), 1, Integer::sum) - 1) % 5 == 0) playYourBlip(speaker, letter);
});Speech.BEGAN fires once per line, before any of it is on screen. Speech.REVEALED fires for every letter as it appears. Both fire for a conversation and for an overheard murmur alike, so one listener covers both.
Keep your own tally rather than working the cadence out from `at`
at counts every character there is, spaces and punctuation included, so blipping on every fifth of those is a beat too quick and leaves a hole wherever the writing has a gap. Counting only what should sound spaces the blips five letters apart however much punctuation is between them.
There is no voice field on a dialogue or a murmur. A voice is a fact about who is talking, not about which file they are reading from.
Design rules worth knowing
Things the API assumes, which will save you a confusing hour.
The client is told nothing it does not need
It receives the words, the choice labels, and whether each label leads to another screen. Never the actions, never the conditions, never which entity the server attached the conversation to.
A close packet is checked against the conversation the server opened, and the answer counts only as a position in the list the server itself sent, so a choice whose conditions failed cannot be picked by naming its index.
The one exception is the list of entity kinds something is bound to, sent on join and after a reload. It buys the client the ability to leave a click alone, and gives away no more than a player would learn by clicking one of each.
Conditions are weighed twice
As the panel is sent, and again when it comes back up. A merchant who sold out while the player was reading is no longer offering to show you.
A conversation with three questions is one visit
times_spoken counts walking up to somebody, not clicking through their branches, and a menu they opened and closed does not count either.
A file that does nothing but branch still needs a way out
A conversation offering no valid choices is given a farewell line automatically, so the player is never stuck on a panel with nothing to press.
Datapack, therefore overridable
A pack can replace any dialogue your mod ships by claiming the same id, and /reload re-reads all of it. Clients need no copy of anything.
What holds a speaker still
A mob with somebody's attention has its AI step cancelled outright, which is the whole of standing still and facing them. Nothing in vanilla does this: marking a villager as trading does not, because the brain ticks regardless, so without it they walk off to bed mid-sentence.
Register through Attention.HOLDING and your own mob gets the same treatment, and BROKE_OFF when it ends.
