NeoOrigins Pack-Builder Cookbook

A recipe-first tour of NeoOrigins 2.0 for datapack authors. The POWER_TYPES, CONDITIONS, ACTIONS, and EVENTS docs are the reference — this doc is the how do I actually build a thing companion.

Contents:


Zero to origin in five minutes

The smallest viable origin needs three files:

data/
  mypack/
    origins/
      origin_layers/
        origin.json                 # which origins show in the picker
      origins/
        my_origin.json              # the origin itself
      powers/
        my_origin_scale.json        # one or more powers granted by the origin

origin_layers/origin.json — add your origin to the built-in picker layer. Overriding neoorigins:origin (the main picker layer) merges your origins into the vanilla tab instead of giving them their own. Only list the origin: IDs you want visible; the built-in origins stay listed by the core mod’s own origin_layers/origin.json — layer JSONs merge by replacement of the origins array, so copy the full list if you want to control ordering.

{
  "origins": [
    "mypack:my_origin"
  ]
}

origins/my_origin.json — the origin definition the picker renders.

{
  "name": "Stone Walker",
  "description": "Hardened from birth. Cannot drown but takes extra fire damage.",
  "impact": 1,
  "order": 50,
  "powers": [
    "mypack:my_origin_scale",
    "mypack:stone_walker_water_breath",
    "mypack:stone_walker_fire_weak"
  ],
  "icon": {
    "item": "minecraft:stone"
  }
}

powers/my_origin_scale.json — a size-scaling power. Keeps the origin visually distinct.

{
  "type": "neoorigins:size_scaling",
  "name": "Small",
  "description": "90% the size of a regular player.",
  "scale": 0.9,
  "modify_reach": false
}

Drop the datapack into world/datapacks/ (or ship it as a mod resource), /reload, open the origin picker, pick your origin. Done.

From here, everything is just adding more power JSONs and referencing them in the origin’s powers array.


Anatomy of a power file

Every power file follows the same shape:

{
  "type": "<the power type>",
  "name": "...",                    // shown in the origin picker
  "description": "...",             // shown in the origin picker
  "hidden": false,                  // optional  hide from the UI
  // ...type-specific fields...
}

The type field is the only mandatory part of the skeleton. The rest of the JSON is whatever fields that power type accepts — see POWER_TYPES.md for the full per-type field catalogue.

Three shapes of power, in rough order of what you’ll reach for:

  1. Always-on / passive — things that are true while the origin is held. attribute_modifier, persistent_effect, condition_passive, size_scaling, prevent_action.
  2. Event-triggered — things that run in response to something the player does. action_on_event, action_on_hit.
  3. Active ability — things the player fires with a keybind. active_ability + entity_action.

Each of the 10 recipes below is one of these three shapes.


Recipes

1. “Passive health boost” — +4 max HP

{
  "type": "neoorigins:attribute_modifier",
  "name": "Tough Skin",
  "description": "+2 hearts maximum health.",
  "attribute": "minecraft:max_health",
  "amount": 4,
  "operation": "add_value"
}

2. “Always has night vision”

{
  "type": "neoorigins:enhanced_vision",
  "name": "Night Adapted",
  "description": "See clearly in the dark.",
  "exposure": 0.7
}

enhanced_vision is the 2.0 replacement for the legacy night_vision / glow toggle. It sets a brightness floor without the intrusive full-white overlay of the vanilla Night Vision effect.

3. “Carnivore — can only eat meat”

{
  "type": "neoorigins:food_restriction",
  "name": "Carnivore",
  "description": "Can only eat meat.",
  "mode": "whitelist",
  "item_tag": "#minecraft:meat"
}

For finer control, list items explicitly under item_tag as an array. Tags (prefixed with #) and bare item IDs can be mixed — the player can eat anything matching any entry.

3b. “Restricted diet + make non-food items edible” (Skeleton pattern)

Combine food_restriction to block normal food with edible_item to make non-food items consumable. The Skeleton origin uses this pattern: only bone meal, rotten flesh, and spider eyes are edible.

Step 1 — Restrict to a whitelist tag:

Create the item tag at data/mypack/tags/item/skeleton_foods.json:

{
  "replace": false,
  "values": [
    "minecraft:bone_meal",
    "minecraft:rotten_flesh",
    "minecraft:spider_eye"
  ]
}

Step 2 — Block all food except the whitelist:

{
  "type": "neoorigins:food_restriction",
  "mode": "whitelist",
  "item_tag": "mypack:skeleton_foods"
}

Step 3 — Make bone meal edible (rotten flesh and spider eye are already vanilla food items, but bone meal is not):

{
  "type": "neoorigins:edible_item",
  "tags": ["mypack:skeleton_bone_meal"],
  "nutrition": 3,
  "saturation": 0.4,
  "always_edible": true
}

With the companion tag at data/mypack/tags/item/skeleton_bone_meal.json:

{ "replace": false, "values": ["minecraft:bone_meal"] }

The origin JSON references both powers — food_restriction blocks normal eating, edible_item grants the bone meal override. The whitelist tag must include ALL items the player can eat (both vanilla food and edible-item-promoted items).

3c. “Food restores more hunger and saturation”

Use action_on_event with the MOD_FOOD_NUTRITION event to boost how much hunger and saturation every food item restores. The modifier multiplies the base nutrition value, so 2.0 doubles all food.

{
  "type": "neoorigins:action_on_event",
  "name": "Iron Stomach",
  "description": "All food restores 50% more hunger.",
  "event": "MOD_FOOD_NUTRITION",
  "modifier": {
    "operation": "multiply",
    "value": 1.5
  }
}

To also boost saturation, add a second power with MOD_FOOD_NUTRITION targeting saturation, or pair with neoorigins:modify_food in a FOOD_EATEN action for a flat bonus on top:

{
  "type": "neoorigins:action_on_event",
  "name": "Well Fed",
  "description": "Gain +2 extra saturation whenever you eat.",
  "event": "FOOD_EATEN",
  "entity_action": {
    "type": "neoorigins:modify_food",
    "food": 0,
    "saturation": 2.0
  }
}

4. “Fire weakness — +50% damage from fire”

{
  "type": "neoorigins:modify_damage",
  "name": "Flammable",
  "description": "Takes +50% damage from fire.",
  "direction": "in",
  "damage_type": "#minecraft:is_fire",
  "multiplier": 1.5
}

Tag-based damage filters (#minecraft:is_fire) catch IN_FIRE + ON_FIRE + LAVA + HOT_FLOOR + FIREBALL in one rule. Bare damage_type strings ("lava") match only that specific source.

5. “Slow fall from high places”

{
  "type": "neoorigins:persistent_effect",
  "name": "Hollow Bones",
  "description": "Always falls slowly.",
  "effect": "minecraft:slow_falling",
  "amplifier": 0,
  "show_icon": false,
  "toggleable": false
}

persistent_effect keeps the effect refreshed automatically (default every 300 ticks). Set show_icon: false so the effects HUD doesn’t clutter. Set toggleable: false so the player can’t turn it off.

6. “Heal in water”

{
  "type": "neoorigins:condition_passive",
  "name": "Aquatic Recovery",
  "description": "Regenerates while submerged in water.",
  "condition": { "type": "neoorigins:submerged_in", "fluid": "minecraft:water" },
  "entity_action": { "type": "neoorigins:feed", "amount": 0.5 },
  "interval_ticks": 40
}

condition_passive is the workhorse 2.0 type — pair any condition with any action, and it fires every interval_ticks when the condition holds. Replaces the old biome_buff, regen_in_fluid, burn_at_health_threshold, and several others.

7. “On kill: heal 2 hearts”

{
  "type": "neoorigins:action_on_event",
  "name": "Blood Diet",
  "description": "Feed on the fallen.",
  "event": "kill",
  "entity_action": { "type": "neoorigins:feed", "amount": 4 }
}

See EVENTS.md for the full list of event keys. Common ones: kill, hit_taken, attack, jump, land, food_eaten, block_break.

8. “Active ability — short dash forward”

{
  "type": "neoorigins:active_ability",
  "name": "Dash",
  "description": "Press the skill key to dash forward.",
  "cooldown_ticks": 60,
  "hunger_cost": 1,
  "entity_action": {
    "type": "neoorigins:dash",
    "strength": 1.2,
    "allow_vertical": true
  }
}

Active abilities bind to the primary skill keybind by default. Set key: "secondary" for a second-slot binding (Y by default).

9. “Summon a minion”

{
  "type": "neoorigins:summon_minion",
  "name": "Raise Skeleton",
  "description": "Call a skeleton guardian.",
  "mob_type": "minecraft:skeleton",
  "max_count": 2,
  "cooldown_ticks": 400,
  "hunger_cost": 6,
  "despawn_ticks": 12000,
  "death_damage": 2.0,
  "mainhand": "minecraft:bow",
  "head": "minecraft:iron_helmet"
}

Summons get their AI rewritten automatically — they won’t attack the summoner, and they fight back against whatever the summoner is fighting. death_damage is the HP penalty when the minion falls in combat.

10. “Lose 1 HP every 2s while in daylight” (Vampire pattern)

{
  "type": "neoorigins:condition_passive",
  "name": "Sun Allergy",
  "description": "Takes damage while exposed to daylight.",
  "condition": { "type": "neoorigins:exposed_to_sun" },
  "entity_action": { "type": "neoorigins:damage", "amount": 1, "source": "on_fire" },
  "interval_ticks": 40
}

neoorigins:exposed_to_sun is the canonical day-exposure condition — it checks both time-of-day and that the block column above the player lets skylight through.

11. “Projectile that drowns enemies in an AoE at the impact point”

Fires a snowball-sized projectile; where it lands, every undead in a 5-block radius takes magic damage and gets heavy slowness for six seconds. This is one power — an active ability whose action fires a projectile with an on_hit_action attached.

{
  "type": "neoorigins:active_ability",
  "name": "Drowning Bolt",
  "description": "Fires a bolt that drowns enemies where it lands.",
  "cooldown_ticks": 200,
  "hunger_cost": 3,
  "entity_action": {
    "type": "neoorigins:spawn_projectile",
    "entity_type": "minecraft:snowball",
    "speed": 1.8,
    "inaccuracy": 0.2,
    "on_hit_action": {
      "type": "neoorigins:area_of_effect",
      "radius": 5.0,
      "include_source": false,
      "entity_action": {
        "type": "neoorigins:if_else",
        "condition": { "type": "neoorigins:target_group", "group": "undead" },
        "if_action": {
          "type": "neoorigins:and",
          "actions": [
            { "type": "neoorigins:apply_effect",
              "effect": "minecraft:slowness",
              "duration": 120,
              "amplifier": 2,
              "show_icon": true },
            { "type": "neoorigins:damage",
              "amount": 1.0,
              "source": { "name": "drown" } }
          ]
        }
      }
    }
  }
}

The wiring (key piece introduced in 2.0.0):

  • on_hit_action — any action, fired when the projectile lands. Stored on the projectile at spawn time, drained on impact.
  • Impact-centered AoEarea_of_effect inside an on_hit_action automatically centers on the impact point, not the caster’s position. Works because the projectile-impact dispatcher installs a ProjectileHitContext on the action-context holder before invoking your action; area_of_effect reads it and rebuilds its AABB.
  • Undead-only filterneoorigins:if_else with target_group: undead wraps the damage+effect so normal mobs and players aren’t caught.
  • Drown-tagged damagesource: { name: "drown" } makes the hit read as drowning, with the correct sound and hit indicator.

Tuning notes:

  • radius: 5.0 is pragmatic. Smaller feels weak, larger hits from across the room.
  • duration: 120 = 6 seconds of Slowness II. Persistent enough to feel impactful, short enough that the next cooldown matters.
  • amount: 1.0 per hit, single-shot. For an attrition-style drowning, wrap in a condition_passive that fires at an interval while a marker entity exists at the impact point — more plumbing.

For a fully custom projectile (homing, chaining, trail effects), use a custom entity type that extends AbstractNeoProjectile — see JAVA_API.md. The reference neoorigins:homing_projectile entity ships with the mod and demonstrates the pattern; pack authors reference it directly:

{
  "type": "neoorigins:spawn_projectile",
  "entity_type": "neoorigins:homing_projectile",
  "speed": 1.2,
  "on_hit_action": { "type": "neoorigins:heal", "amount": 2 }
}

The Java subclass handles per-tick AI (in this case, steering toward the nearest living entity); the DSL on_hit_action still fires independently on impact, so custom projectiles integrate cleanly with the rest of the 2.0 action pipeline.

12. “Black hole — pull everything nearby toward a point and damage the center”

Active ability that spawns a lingering gravity well at the caster’s look target. Pulls for two seconds, then expires.

{
  "type": "neoorigins:active_ability",
  "name": "Singularity",
  "description": "Collapse space at your feet for a moment.",
  "cooldown_ticks": 400,
  "hunger_cost": 6,
  "entity_action": {
    "type": "neoorigins:spawn_black_hole",
    "radius": 8.0,
    "duration_ticks": 40,
    "pull_strength": 2.5,
    "damage_per_tick": 2.0,
    "effect_type": "sonic"
  }
}

Tuning notes:

  • duration_ticks: 40 = 2 seconds. Longer reads as overpowered fast.
  • Combine with an on_hit_action on a spawn_projectile to drop the black hole wherever the projectile lands instead of the caster’s feet — swap entity_action for:
    { "type": "neoorigins:spawn_projectile",
      "entity_type": "neoorigins:magic_orb",
      "speed": 2.0,
      "on_hit_action": { "type": "neoorigins:spawn_black_hole", "radius": 6.0, "duration_ticks": 40 } }
    

13. “Tornado — fling entities in a cone”

{
  "type": "neoorigins:active_ability",
  "name": "Whirlwind",
  "description": "Summon a tornado where you're looking.",
  "cooldown_ticks": 300,
  "hunger_cost": 5,
  "entity_action": {
    "type": "neoorigins:spawn_projectile",
    "entity_type": "neoorigins:magic_orb",
    "speed": 1.5,
    "effect_type": "frost",
    "on_hit_action": {
      "type": "neoorigins:spawn_tornado",
      "radius": 6.0,
      "duration_ticks": 80,
      "pull_strength": 1.5,
      "lift_strength": 0.8,
      "spin_strength": 0.8,
      "damage_per_interval": 1.5
    }
  }
}

The wiring:

  • Projectile carries the tornado to wherever the caster aims.
  • on_hit_action drops a spawn_tornado at the impact point (the projectile-hit context auto-rebases the spawn to the hit location).
  • lift_strength: 0.8 lifts targets ~2 blocks over the tornado’s lifetime — enough to disorient without throwing them across the map.

14. “Lingering damage cloud at projectile impact”

Classic poison/acid cloud pattern — the projectile carries a cloud, the cloud ticks its action on an interval until it expires.

{
  "type": "neoorigins:active_ability",
  "name": "Spore Bolt",
  "description": "Fires a spore that lingers where it lands.",
  "cooldown_ticks": 200,
  "hunger_cost": 3,
  "entity_action": {
    "type": "neoorigins:spawn_projectile",
    "entity_type": "neoorigins:magic_orb",
    "speed": 1.8,
    "effect_type": "spore",
    "on_hit_action": {
      "type": "neoorigins:spawn_lingering_area",
      "radius": 4.0,
      "duration_ticks": 120,
      "interval_ticks": 20,
      "particle_type": "minecraft:spore_blossom_air",
      "entity_action": {
        "type": "neoorigins:area_of_effect",
        "radius": 4.0,
        "include_source": false,
        "entity_action": { "type": "neoorigins:apply_effect",
          "effect": "minecraft:poison", "duration": 60, "amplifier": 1 }
      }
    }
  }
}

The nested area_of_effect inside the cloud’s entity_action centers on the cloud’s position (not the caster’s), because the lingering-area tick passes the cloud’s location to the action context holder.


15. Toggleable abilities (no keybind slot)

A neoorigins:toggle power is a named boolean stored per-player. It doesn’t appear on the skill bar and doesn’t consume a keybind slot — its only job is to be read by other powers’ condition field and written by other powers’ entity_action. This is the cleanest way to give pack authors a stance, a stealth flag, a mark, or any “is this mode on right now?” gate without burning a hotbar key.

The pattern is always three pieces:

  1. The flag itself — a neoorigins:toggle power. Optional default sets the value reads see before the flag has ever been flipped. Set "hidden": true to keep the flag off the origin info panel — almost always what you want for an internal flag.
  2. A way to read itneoorigins:power_active { power: "..." } inside any other power’s condition.
  3. A way to flip itneoorigins:toggle { power: "..." } inside any entity_action (active ability, action_on_hit, action_on_event, etc.). These glue powers also typically want "hidden": true since the player only needs to see the user-facing rollup.

Example A — Toggle-gated wall climbing

A passive that lets the player climb walls, but only while a separate “climb mode” flag is on. The active ability flips the flag.

// data/mypack/origins/powers/climb_mode.json  the flag (visible so the player sees it cycle)
{
  "type": "neoorigins:toggle",
  "default": false,
  "name": "Climb Mode",
  "description": "While active, you can climb walls."
}
// data/mypack/origins/powers/wall_climb.json  gated capability
{
  "type": "neoorigins:wall_climbing",
  "condition": {
    "type": "neoorigins:power_active",
    "power": "mypack:climb_mode"
  },
  "name": "Spider Grip",
  "description": "Climb any wall while Climb Mode is on."
}
// data/mypack/origins/powers/toggle_climb.json  the keybind
{
  "type": "neoorigins:active_ability",
  "cooldown_ticks": 10,
  "entity_action": {
    "type": "neoorigins:toggle",
    "power": "mypack:climb_mode"
  },
  "name": "Toggle Climb Mode",
  "description": "Press to toggle wall climbing on/off."
}

The wall-climb power does the work; the toggle is just a data flag. Swap wall_climbing for any condition-gated power (attribute, persistent_effect, etc.) and the same pattern works.

Example B — Stance switch (Hunter’s Mark style)

A flag that’s flipped on by attacking and back off after the next kill — no keybind required. While the flag is on, the player gains Strength I against all targets.

// data/mypack/origins/powers/marked.json  the internal flag
{
  "type": "neoorigins:toggle",
  "default": false,
  "hidden": true
}
// data/mypack/origins/powers/mark_on_hit.json  flip on when hitting (visible: this is the user-facing description)
{
  "type": "neoorigins:action_on_hit",
  "entity_action": {
    "type": "neoorigins:toggle",
    "power": "mypack:marked",
    "value": true
  },
  "name": "Hunter's Mark",
  "description": "Marks the next target in line."
}
// data/mypack/origins/powers/mark_clear_on_kill.json  internal: flip off on kill
{
  "type": "neoorigins:action_on_kill",
  "hidden": true,
  "entity_action": {
    "type": "neoorigins:toggle",
    "power": "mypack:marked",
    "value": false
  }
}
// data/mypack/origins/powers/marked_strength.json  gated buff
{
  "type": "neoorigins:persistent_effect",
  "condition": { "type": "neoorigins:power_active", "power": "mypack:marked" },
  "effects": [
    { "effect": "minecraft:strength", "amplifier": 0 }
  ],
  "toggleable": false,
  "hidden": true,
  "name": "Marked Strength",
  "description": "Strength I while a target is marked."
}

Note value: true / value: false to set explicitly, vs. leaving value off to flip whatever the current state is. Setting explicitly is idempotent — useful for one-shot triggers like the action_on_hit above that should always force the mark on regardless of prior state.

Why use this over AbstractTogglePower subclasses (e.g. flight, item_magnetism)? Those occupy a keybind slot and are best for one ability per origin. neoorigins:toggle is for fan-out: a single flag that multiple powers gate on, or a flag flipped by something other than the keybind (an event, a hit, a tick condition).


Bare-hand stone mining (Caveborn pattern)

Make the player’s empty hand behave as a vanilla tool at any tier, enabling proper drops and mining speed without holding an actual item.

{ "type": "neoorigins:bare_hand_tool", "tool": "minecraft:stone_pickaxe" }

Stack multiple instances to emulate several tool types simultaneously (pickaxe + axe + shovel). Config accepts any vanilla tool item ID; the runtime reads the item’s tool component to determine correct-for-drops and break speed. Only fires while the main hand is empty.


Eat stone for food, eat diamonds for Luck (Caveborn consumables)

Chained pattern: an edible_item power makes items consumable, and an action_on_event power listens on ITEM_USE_FINISH to apply a bonus when a matching item is eaten. Cross-reference using a custom item tag in food_item_in_tag.

Edibility side:

{ "type": "neoorigins:edible_item",
  "tags": ["mypack:eat_diamond"],
  "nutrition": 6, "saturation": 1.2, "always_edible": true }

Bonus side:

{ "type": "neoorigins:action_on_event",
  "event": "item_use_finish",
  "entity_action": {
    "type": "neoorigins:if_else",
    "condition": { "type": "neoorigins:food_item_in_tag",
                   "tag": "#mypack:eat_diamond" },
    "if_action": { "type": "neoorigins:apply_effect",
                   "effect": "minecraft:luck", "duration": 6000, "amplifier": 1 }
  }
}

Ship the item tag at data/mypack/tags/item/eat_diamond.json.


Fortune-from-effect (Mining Fortune gated by a buff)

Combine with the recipe above: while the player has minecraft:luck (granted by the consumable), ore blocks drop as if mined with a real Fortune pickaxe. Vanilla ApplyBonusCount.ORE_DROPS math, so the distribution matches a Fortune enchantment exactly.

{ "type": "neoorigins:fortune_when_effect",
  "effect": "minecraft:luck", "level": 2, "target": "#c:ores" }

Ancient debris is hardcoded-excluded (vanilla parity — Fortune doesn’t affect netherite drops). Pack authors can narrow target to e.g. #minecraft:diamond_ores for a diamond-only bonus.


Moon-blessed armor (condition-gated attribute)

Attribute modifiers accept a condition that edge-triggers at ~5-tick intervals. Pair with the new neoorigins:night condition for “stronger after dark” flavor.

{ "type": "neoorigins:attribute_modifier",
  "attribute": "minecraft:armor",
  "amount": 4.0, "operation": "add_value",
  "condition": { "type": "neoorigins:night" } }

The modifier is applied/removed automatically as daytime flips. Any condition works here — neoorigins:thundering for storm-blessed equipment, neoorigins:climbing for scaling-specific buffs, etc.


Sleepless origin with bed-anchored respawn

prevent_action SLEEP blocks sleep transitions. The SleepPreventionEvents handler automatically sets the player’s respawn point at the bed before cancelling, so players still get the anchor benefit — great for vampires, phantoms, wardens.

{ "type": "neoorigins:prevent_action", "action": "SLEEP" }

Feedback message “You don’t need sleep — but you remember this place.” fires on bed interaction.


Elytra-style flight without an elytra (Phantom/Elytrian)

Grant gliding behavior — press jump while falling, spread wings like a vanilla elytra user. No chest-slot requirement.

{ "type": "neoorigins:natural_glide" }

Pair with neoorigins:elytra_boost for launch-speed amplification. Contrast: neoorigins:flight is creative-mode-style hover (hold space to ascend). natural_glide is pitch-based gliding, the real-elytra feel.


Forward dash in look direction

The neoorigins:dash action projects a strength magnitude along the player’s look vector. Canonical V2 replacement for active_dash.

{ "type": "neoorigins:active_ability",
  "cooldown_ticks": 60, "hunger_cost": 2,
  "entity_action": { "type": "neoorigins:dash",
                     "strength": 2.2,
                     "allow_vertical": true } }

Set allow_vertical: false for ground-lock dashes that ignore look pitch. Sets hurtMarked = true internally so client prediction doesn’t discard the impulse.


Warm near a campfire (near_block condition)

neoorigins:near_block scans a cubic radius for matching blocks and fires when any are found. Accepts any combination of block/blocks/ tag/tags fields with logical OR.

{ "type": "neoorigins:condition_passive",
  "condition": {
    "type": "neoorigins:near_block",
    "tags": ["minecraft:campfires", "#c:fire"],
    "radius": 5
  },
  "entity_action": {
    "type": "neoorigins:apply_effect",
    "effect": "minecraft:fire_resistance",
    "duration": 100, "amplifier": 0
  },
  "interval": 20
}

Radius is capped at 8 to keep the per-tick scan cheap.


Essence Evolution (tier progression)

NeoOrigins ships an Essence Evolution system — a kill-based tier progression that lets origins grow stronger over time. Players kill mobs to accumulate essence, receive milestone chat messages, and get prompted to evolve when they hit a threshold.

Tiers

Tier Name Default Kill Threshold Chat Color
0 (base)
1 Evolved 1,000 Green
2 Ascended 2,500 Light Purple
3 Apex 5,000 Gold

Thresholds are configurable in config/neoorigins-common.toml:

[evolution]
    enabled = true
    tier_1_kills = 1000
    tier_2_kills = 2500
    tier_3_kills = 5000
    message_interval = 100

message_interval controls how often the “essence strengthening” flavor message appears in chat (default: every 100 kills).

How it works

  1. Player kills a mob (non-player entities only; PvP kills don’t count).
  2. Kill counter increments. At every message_interval kills the player sees a milestone message.
  3. When the counter reaches a tier threshold, the player gets a chat prompt with clickable [EVOLVE] / [DECLINE] buttons.
  4. Accepting grants the tier’s add powers and revokes its remove powers (defined in the origin JSON). Declining defers — the player can accept later via command.
  5. Using an Orb of Origin resets both kills and tier to zero.

Commands

Player commands (permission level 0):

  • /neoorigins evolve accept — accept a pending evolution prompt
  • /neoorigins evolve decline — decline (can re-accept later)
  • /origin evolve accept / /origin evolve decline — legacy aliases

Admin commands (permission level 2):

  • /neoorigins evolve <player> <tier> — force-set tier (base/0, evolved/1, ascended/2, apex/3)
  • /neoorigins evolve query <player> — show current kills and tier

Adding evolution tiers to an origin

Add a tier_powers array to your origin JSON. Each entry specifies which powers to add and which to remove at that tier. Tiers are cumulative — tier 2 includes tier 1 changes plus its own.

{
  "name": "origins.mypack.shadow.name",
  "description": "origins.mypack.shadow.description",
  "impact": "medium",
  "powers": [
    "mypack:shadow_stealth",
    "mypack:shadow_attack",
    "mypack:shadow_weakness"
  ],
  "tier_powers": [
    {
      "tier": 1,
      "add": ["mypack:shadow_evolved_attack"],
      "remove": ["mypack:shadow_attack"]
    },
    {
      "tier": 2,
      "add": [
        "mypack:shadow_ascended_attack",
        "mypack:shadow_ascended_stealth"
      ],
      "remove": [
        "mypack:shadow_evolved_attack",
        "mypack:shadow_stealth"
      ]
    },
    {
      "tier": 3,
      "add": [
        "mypack:shadow_apex_attack",
        "mypack:shadow_apex_immunity"
      ],
      "remove": [
        "mypack:shadow_ascended_attack",
        "mypack:shadow_weakness"
      ]
    }
  ]
}

tier_powers fields:

Field Type Description
tier int (1–3) The tier this overlay applies at
add array of power IDs Powers granted when evolving to this tier
remove array of power IDs Powers revoked when evolving to this tier

Common patterns:

  • Stat growth: replace a base attribute_modifier with a stronger one at each tier (e.g. +2 HP → +4 HP → +8 HP). Put the base in powers, the evolved version in tier 1 add and the base in tier 1 remove.
  • Weakness removal: list a weakness power in a later tier’s remove to shed it as a reward (e.g. Apex vampires lose sun damage).
  • New abilities: add an active_ability or persistent_effect at a tier without removing anything — pure upgrade.

The evolution power JSONs themselves are standard power files — no special format. Any power type works as a tier reward.

Built-in evolution powers

NeoOrigins ships ~250 evolution power JSONs covering all bundled origins. These live at:

data/neoorigins/origins/powers/<origin>_evolved_*.json
data/neoorigins/origins/powers/<origin>_ascended_*.json
data/neoorigins/origins/powers/<origin>_apex_*.json

All bundled origins have full three-tier evolution tracks. Pack authors can override them with higher-priority datapacks or use them as examples for their own origins.


Built-in mechanics

Undead potion reversal

Origins with "entity_group": "undead" (Revenant, Necromancer, Skeleton) automatically receive vanilla-style undead potion interactions:

  • Poison — immune (no effect)
  • Regeneration — immune (no effect)
  • Instant Health — deals damage instead of healing (vanilla formula: 6 × 2^amplifier)
  • Instant Damage — heals instead of dealing damage (same formula)
  • Food healing — works normally (natural regen from saturation is not blocked)

This is built into the engine for any origin that declares "entity_group": "undead" — no extra power JSON is needed.

Aquatic origin shared powers

All built-in ocean origins (Merling, Siren, Kraken, Abyssal) share these passive powers in addition to their unique abilities:

Power Effect
neoorigins:aquatic_underwater_mining Mines at full speed while submerged (Aqua Affinity)
neoorigins:aquatic_depth_strider +0.15 water movement efficiency (Natural Swimmer)
neoorigins:aquatic_fish_diet Fish-only diet (gated by ocean_origins.fish_diet_required config)
neoorigins:aquatic_fish_diet_bonus Raw cod/salmon as nourishing as cooked

Custom aquatic origins can reference any of these shared powers by ID.

Moisture system: ocean origins with breath_out_of_fluid replenish moisture from water blocks, rain, bubble columns, and water cauldrons.


Testing & debugging

Hot-reload

/reload reloads all datapacks including power definitions. You don’t need to rejoin. If a power you just added isn’t showing up, check the server log for a [CompatB] or [2.0-legacy] warning — JSON parse errors log once at reload.

The deprecation log

Every legacy type emits a single one-shot warning at first use:

[2.0-legacy] power type 'neoorigins:thorns_aura' is deprecated — remap to 'neoorigins:action_on_event' (first seen on power 'mypack:spiky_skin')

Grep your logs for [2.0-legacy] to get your migration punch list. See MIGRATION.md for the full remap table.

Forcing an origin for testing

Origin-picker state lives in the player data attachment. The fastest iteration loop:

  1. Give yourself a stack of neoorigins:orb_of_origin.
  2. Use the orb to reroll. Each use opens the picker; the XP cost applies only when you commit a choice.
  3. Pick, test, reroll, repeat.

/function from an event

Chain any shell command out of a power using execute_command:

{
  "type": "neoorigins:action_on_event",
  "event": "kill",
  "entity_action": {
    "type": "neoorigins:execute_command",
    "command": "function mypack:on_kill_callback"
  }
}

The command runs at server-level permissions (level 2), positioned at and targeting the player.


Common pitfalls

“My power JSON parsed but nothing happens.” Most often the condition you attached is evaluating false every tick. Add a neoorigins:always_true placeholder condition to confirm the wiring works, then narrow it back down.

“My size-scaling power makes me punch through walls.” Set modify_reach: false on the size_scaling config. Reach scaling was removed from size in v1.14 because it was confusing — it now only scales when explicitly enabled.

“My active ability triggers but the server disagrees with the client.” add_velocity needs hurtMarked to survive the client’s next physics tick. The 2.0 alias handles this; if you’re on the raw spawn_projectile action, set set: false and keep the magnitude conservative (<1.5 per axis).

“My action_on_event fires twice on respawn.” onLogin and onRespawn both default to calling onGranted. If your event registers a handler in onGranted, the handler count doubles on each respawn. Use action_on_event (which is idempotent by construction) rather than registering listeners from a custom power.

“My custom power type shows as unknown.” The type field is case-sensitive and must be fully qualified: "neoorigins:persistent_effect", not "persistent_effect". Check the [CompatB] log if a type you expected to be registered isn’t resolving.

“Packets say UI sent but players see nothing.” Most often: the client is running a much older version of NeoOrigins than the server. The picker payload is versioned — mismatched mod versions drop the payload on the client side.


Where to go next

  • POWER_TYPES.md — every power type with full field tables. When a recipe here mentions a type, the details live there.
  • CONDITIONS.md — the 80+ condition verbs you can use in condition fields.
  • ACTIONS.md — the 40+ action verbs you can use in entity_action fields.
  • EVENTS.md — the event keys for action_on_event.
  • MIGRATION.md — if you’re porting a pre-2.0 pack or an upstream Origins/Apoli/Apugli pack.
  • ARCHITECTURE.md — for understanding how powers are resolved, dispatched, and cached (useful for pack-author debugging but not required reading).

The source of truth for every alias, verb, and event key is the Java code under src/main/java/com/cyberday1/neoorigins/ — if a doc drifts from the code, the code wins. When in doubt, grep.