Custom Classes

In NeoOrigins, a class is not a separate system; it’s an ordinary origin that lives in the special neoorigins:class layer. Every player picks an origin (layer 1) and a class (layer 2); the class screen is the second selection screen shown on first join.

Because a class is just an origin, everything in PACK_FORMAT.md about Origin JSON and Power JSON applies directly. This page covers only what’s class-specific.

The class layer

The built-in class layer is defined at data/neoorigins/origins/origin_layers/class.json with order: 2. Most class powers are passive or condition-gated, but a class may also carry an active power.

A class gets one keybind slot. The class layer does not use the six skill slots that the origin layer does. Instead, the first active power in a class’s powers list is bound to the dedicated Class Skill key (default H), shown as C in the HUD when the key is unbound. Every later active power in the same class is silently left unbound, so if you want two of them, only the first one will ever fire.

The built-in Step Assist switch on the Explorer, Rogue and Scout is the worked example: a hidden neoorigins:toggle holds the state, an neoorigins:active_ability flips it, and the step-height attribute_modifier is gated on the toggle.

That pattern (a hidden toggle plus an active that flips it) is the intended way to give a class a switchable passive, because it keeps the passive itself condition-gated while spending only the one class slot.

The Rogue is the one built-in class that wants two: it carries both the Step Assist switch and class_rogue_stealth. Step Assist is listed first and holds the key, so Stealth has none. Both powers are still granted, and because a toggle no key can reach is switched back on at every login (see the toggle notes in POWER_TYPES.md), the Rogue’s Stealth is simply always on: sneak for ten seconds and you turn invisible. A Rogue who switched Stealth off before 2.2.28 gets it back the next time they log in.

You do not need to edit the built-in class.json. Any layer file whose path is class is automatically folded into neoorigins:class, regardless of namespace. So ship your own:

data/<yourpack>/origins/origin_layers/class.json

{
  "name": "origins.layer.class",
  "origins": [
    "yourpack:class_alchemist"
  ]
}

Your class is appended to the existing list: all built-in classes are kept, and you never have to maintain a copy of the mod’s list. (Opt out of the fold with "standalone": true if you deliberately want a separate screen.)

Alternative: overriding the built-in layer

A file at the exact built-in path data/neoorigins/origins/origin_layers/class.json is merged with the built-in one like any other same-id layer file (its origins are appended). To replace the list entirely, add "replace": true; you must then re-list every built-in class you want to keep, and re-sync on every mod update. Prefer the additive method above unless you specifically want to remove built-in classes (the [classes] config toggles are usually the better tool for that).

The class origin JSON

Identical to any origin (data/<yourpack>/origins/origins/<id>.json). Conventions used by the built-ins:

{
  "name": "origins.yourpack.class_alchemist.name",
  "description": "origins.yourpack.class_alchemist.description",
  "icon": "minecraft:brewing_stand",
  "impact": "none",
  "order": 21,
  "powers": [
    "yourpack:class_alchemist_resilience",
    "yourpack:class_alchemist_antidote"
  ],
  "upgrades": []
}
  • icon: item shown in the class picker.
  • impact: the built-in classes use "none", except Fisher, Mason and Paladin, which use "medium".
  • order: position in the class screen (built-ins occupy 1–20; use 21+ to append after them).
  • powers: passive, condition-gated or attribute powers, plus at most one active power, which takes the Class Skill key (see above).
  • upgrades: optional advancement-driven promotion to another class; see the working examples/class_tier_up/ datapack and the Upgrades section of the examples README.

Naming convention: prefix the origin id and its powers with class_ (class_alchemist, class_alchemist_resilience). Not required by the code, but it keeps packs consistent with the built-ins.

Stacking with the origin layer

A class and an origin are separate layers, and their powers add together; neither replaces the other. A player who is a Golem (origin, 1.3x size) and a Titan (class, 1.25x size) ends up 1.55x, and re-picking either layer leaves the other layer’s contribution alone. The same holds for attribute_modifier bonuses: health, armor and reach from the origin survive a class change.

Design classes on that assumption. If a class is meant to be an alternative to something an origin already grants rather than an addition to it, express that with a condition on the class power, not by expecting it to overwrite the origin’s.

Lang keys

Same derivation as any origin/power:

  • origins.<namespace>.<class_id>.name / .description
  • power.<namespace>.<power_id>.name / .description

Or use literal components ({"text": "Alchemist"}) directly in the JSON if you don’t want a resource/language pack: handy for self-contained datapacks.

Defaults and config

  • No class chosen: if a player closes the picker with an origin but no class, NeoOrigins auto-assigns neoorigins:class_nitwit (a deliberate no-effect default) so starting equipment and pending grants still resolve.
  • Disabling built-ins: the [classes] section in config/neoorigins/content.toml toggles each built-in class. Disabled classes stay registered (so /neoorigins set can still assign them) but are hidden from the selection screen.
  • No classes at all: if every class is disabled, the class selection screen is skipped entirely and only the origin layer is shown.
  • Skip initial selection: the [skip_initial_selection] section in config/neoorigins/gameplay.toml has an enabled flag (default false). When set to true, new players spawn with no origin and the selection screen never opens on first join; they play as an origin-less player until granted one later (for example via an Orb of Origin or /neoorigins set). Unlike auto-human mode this assigns nothing; the player’s selection is marked complete, so the join check doesn’t re-prompt on every relog. It takes priority over auto-human and random-assignment modes.

The Orb of Origin

neoorigins:orb_of_origin re-picks only the neoorigins:origin layer. Right-clicking it reopens the selection screen scoped to that layer alone, so the player’s class (and any other layer) is kept: changing class is the Orb of Class’s job. Any sub-layer whose conditions no longer pass under the new origin is cleared automatically. Like the Orb of Class the commit is deferred, so closing the picker without choosing is a free cancel (the orb is refunded and the previous origin restored). Cost comes from the [orb_of_origins] section of config/neoorigins/gameplay.toml: with scale_cost = true (the default) it is levels_per_use (default 5) times the player’s previous orb uses, so the first use is free; with scale_cost = false every use costs levels_per_use.

The Orb of Class

neoorigins:orb_of_class is the cheaper, class-only sibling of the Orb of Origin. Right-clicking it resets only the neoorigins:class layer and reopens the picker scoped to just that layer: the player’s main origin is kept, so only the class screen is shown.

  • Cost: a flat XP-level cost, configured by class_levels_per_use in the [orb_of_origins] section of config/neoorigins/gameplay.toml (default 2). Unlike the Orb of Origin’s levels_per_use (default 5, which can ramp with prior uses), the class cost never scales. Creative players pay nothing.
  • Deferred commit: the XP is charged and the orb is consumed only when the player actually picks a new class. Closing the picker without picking is a free cancel: the orb is refunded and the previous class is restored.
  • No class yet: using the orb before a class has been chosen does nothing (there is no class to reset).

Reopening the picker for arbitrary layers

The Orb of Class is one preset of a general mechanism: the picker can be reopened for any layer subset, not just the class layer. Two author paths expose it:

  • Datapack action neoorigins:open_layer_picker: give any power (item use, keybind, on-hit, …) a layers list to re-pick, with commit_mode (deferred/immediate), an XP cost, an optional message shown when the picker opens, and consume_item to spend the triggering item on commit. This lets a pack build its own re-pick items or powers for whatever layers it defines.
  • Admin command /origin gui <player> <layers>: opens the picker scoped to one or more comma- or space-separated layers for a single target player (permission level 2).

Recipe

Same shape as the Orb of Origin, but with ingots instead of blocks:

G D G      G = minecraft:gold_ingot
D N D      D = minecraft:diamond
G D G      N = minecraft:netherite_ingot

See also