Pixel Dream StudiosWiki

For mod developers

Depending on RPG Errands, taking a villager out of the system, reading what a player has done, and adding a fourth kind of task.

Everything on the other pages is a datapack file and needs no Java. This page is for a mod that wants to work with 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-errands:<version>"
    // NeoForge
    implementation "maven.modrinth:rpg-errands:<version>"
}

Everything below is the same class on both loaders. This mod owns no platform service of its own; the two things it cannot do in common code are RPG Dialogue's, and already work on both.

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, errands, dialogues and translations, is yours outright.

On NeoForge, order after RPG Dialogue

ordering = "AFTER" on the rpg_dialogue dependency in your neoforge.mods.toml if you construct anything through its helpers, which this mod does. Constructed first, the helper has no mod event bus to hand its attachments to.

Taking somebody out of it

// Never asked, never marked. The click falls through to whatever else would handle it.
Errands.exempt(entity -> Inns.is(entity, Inns.MUSICIAN));

Somebody halfway through a set does not stop it to ask you to fetch four rabbit hides, and an inn where all three of them do is not an inn with music in it, it is a queue with a soundtrack.

Reach for this only when being asked at all is wrong. Two things that are not reasons:

  • Somebody merely busy. They should still be asked.
  • Somebody whose greeting you have replaced. Already covered, and silently: a mark is only drawn where the conversation that would actually open mentions an errand, so a greeting of yours with no errand choice in it takes them out correctly without registering anything.

Register while your mod initialises. It is asked of every villager in range on every sweep, so keep it cheap. A predicate rather than a set of entities because the answer is about what somebody is doing, and that changes.

The rest

CallAnswers
Errands.between(player, entity)Which of the five states holds.
Errands.standing(player, entity)The errand itself, how many were asked for, and how far along. Null for nothing at all.
Errands.offeredBy(player, entity)What they would ask for today, or null.
Errands.get(id)An errand as a pack wrote it.
Errands.isExempt(entity)Whether anything has claimed them.
ErrandLog.timesRun(player, id)How often this player has run that particular errand.
ErrandLog.totalRun(player)How many they have run for anybody.
Task.register(id, codec)A fourth kind of task.

standing hands back one settled record rather than making you assemble it: which errand, who asked, how many were required(), how many are done(), what remaining() is, whether it has been taken() and whether it is settled(). Which errand somebody wants cannot be read off its own file, since the amount is drawn when the promise is made and how far along the player is depends on the kind of task.

Note that the person standing is asked about may not be the person who asked. A delivery is spoken of and settled by whoever it was carried to, and remains the sender's entry in the ledger.

Adding a task kind

Task is deliberately not sealed, and task takes a whole namespaced id precisely so that it can be somebody else's. A record, a MapCodec and one call:

public record Escort(EntityType<?> who, int away) implements Task {
    public static final MapCodec<Escort> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
            BuiltInRegistries.ENTITY_TYPE.byNameCodec().fieldOf("who").forGetter(Escort::who),
            Codec.intRange(16, 4096).optionalFieldOf("away", 160).forGetter(Escort::away)
    ).apply(instance, Escort::new));

    @Override public MapCodec<? extends Task> codec() { return CODEC; }

    // Where your three shared conversations live, under your own namespace.
    @Override public String shared() { return "villager/errand/escort"; }

    // amount(), exists(), name(), done(): see the javadoc
}

Task.register(ResourceLocation.fromNamespaceAndPath("yourmod", "escort"), Escort.CODEC);

Packs then write { "task": "yourmod:escort", "who": "minecraft:villager", "away": 200 }, and the fields are read flat out of the errand's own file rather than from a block inside it.

MethodFor
amount()How many are wanted, before the day's draw settles on a figure.
exists()Whether there is anything in this world it could mean. False for a tag nothing fills, which is how a kind written for an absent mod lies dormant.
name()What to call it in a sentence, when the errand does not name the haul itself.
done(player, held)How far along the player is, counted the way your kind counts.
shared()Where your three shared conversations live, so a pack using your kind writes none.
settledBy(...)Whether somebody other than the sender is who it is settled with. Only a delivery has anybody to say yes to.
countsKill(...)Whether killing this counts. Nothing else in the world has an opinion.
collect(player, required)Take whatever settling costs, all or nothing, or take nothing at all.

Register while your mod initialises, before any pack is read. A kind nothing has registered is a parse error in every errand that names it.

Adding a condition, value or action

Those registries are RPG Dialogue's, not this mod's, and errands take the whole of them. A condition you register can gate an errand's requires; an action you register can be an errand's rewards, which is how a mod's own currency, skill points or reputation pay out an errand without a line of this mod changing.

See RPG Dialogue for mod developers.

Drawing your own marks

If your mod puts a sprite over a head as well, draw it through RPG Dialogue's WorldIcon, which is what this mod's marks use, and declare the space with WorldText.CROWDING so murmurs lift clear of it. Two copies of that code is how two mods' marks end up at different heights over one villager.

On this page