← LegendQuest ReForged

Writing a skill pack

For mod developers. In the Bukkit era a skill pack was a jar dropped in a folder, classloaded by hand, with one Java class per skill sharing a single instance across every player. In ReForged a skill pack is an ordinary NeoForge mod that depends on LegendQuest - so you get versioning, dependency checking and proper classloading for free, and per-player state is handled by the engine.

Start here

There are two layers, and most "new skills" only need the first.

Before writing any Java, check whether composing the 14 built-in effect types gets you there. A skill is a data file, and the effects list is what makes it behave - most ideas are a combination of things that already exist.

Layer 1 - no code

Compose existing effects in data.

Server owners, or your pack's bundled datapack, can define new skills by composing registered effect types. No jar required at all.

# config/legendquest/skills/fire_dash.yml   (or JSON in any datapack)
name: Fire Dash
type: active            # active | passive | triggered
cooldown: 8000          # all times in milliseconds
mana_cost: 12
effects:
  - { type: "legendquest:leap", power: 2.2, lift: 0.3 }
  - { type: "legendquest:ignite", duration: 2000,
      target: { kind: nearby, radius: 3 } }
  - { type: "legendquest:sound", sound: "minecraft:entity.blaze.shoot" }

Built-in effect types in the legendquest: namespace: damage, heal, potion_effect, leap, teleport, lightning, summon, message, ignite, give_item, sound, particle_line, projectile and run_command. Full options for each are on the content reference.

Targets: self, looking_at (with a range), nearby (with a radius), trigger (the other party of a triggered skill's event) and party.

Triggered skills add a trigger block: trigger: { on: melee_hit, chance: 25.0 }, where on is one of melee_hit, hurt, kill or fall.

Races and classes then grant the skill by id - deliberately the easy part:

skills:
  mypack:fire_dash: { level: 10, cost: 5 }

Layer 2 - a small mod

New effect types: one record, one registration line.

When a skill needs behaviour no existing effect covers, register a new effect type. SkillEffectTypes.register() in your mod constructor is the entire API surface.

@Mod("firepack")
public class FirePack {
    public FirePack(IEventBus modEventBus, ModContainer container) {
        SkillEffectTypes.register(FlameRing.TYPE, FlameRing.CODEC);
    }
}

public record FlameRing(double radius, int flames) implements SkillEffect {
    public static final Identifier TYPE =
            Identifier.fromNamespaceAndPath("firepack", "flame_ring");

    public static final MapCodec<FlameRing> CODEC = RecordCodecBuilder.mapCodec(i -> i.group(
            Codec.DOUBLE.optionalFieldOf("radius", 3.0).forGetter(FlameRing::radius),
            Codec.INT.optionalFieldOf("flames", 8).forGetter(FlameRing::flames))
            .apply(i, FlameRing::new));

    @Override public Identifier type() { return TYPE; }

    @Override
    public void apply(SkillContext ctx) {
        // Runs on the server thread. ctx.caster(), ctx.level(),
        // ctx.skillLevel(), ctx.triggerTarget() are available.
    }
}

Rules of the road

  • Effects are immutable records. Never store player state on the effect - fields are your YAML parameters, nothing else. Per-player state belongs in the engine (cooldowns are already handled) or your own attachment.
  • Register from your mod constructor, before datapacks load. Duplicate ids are refused with a log line, not silently replaced.
  • A typo'd type: in a data file produces a log message listing every known effect type - tell your users to read the server log.
  • In mods.toml, declare a required dependency on legendquest so load order and version ranges are enforced. The old pack system's "load order is pure luck" problem is gone.

Gradle

Use the same ModDevGradle scaffold as LegendQuest itself, and add:

dependencies {
    // consume the published LegendQuest jar (or a maven repo / jarInJar later)
    implementation files("libs/legendquest-<version>.jar")
}

Before you ship it

The one that will catch your users.

A skill-pack jar must be installed on the server AND on every modded client. Miss one client and it fails registry sync with "Unknown skill effect type". Vanilla clients are unaffected - they never see the registry at all, which is the one case that keeps working no matter what you ship.

This is the difference between a skill pack and the genre packs: a datapack is server-side data and needs nothing from anybody, while a skill pack registers new types that a modded client must also know about. If your pack is aimed at a public server with mixed clients, prefer Layer 1 wherever the idea allows it.

A worked example ships on every release as examplepack-1.0.0.jar. Drop it in mods/ next to LegendQuest to see custom effect types working, or read it as a template. It also carries a Stormcaller class under examples/ in the jar - deliberately outside data/, so it never auto-loads; copy it into a datapack if you want it.