NeoOrigins 2.0 Migration Guide

Reference for pack authors updating JSON from pre-2.0 types to the 2.0 generic / composable types.

What changed

Pre-2.0 NeoOrigins had 88 hardcoded PowerType classes, one per behaviour. 2.0 collapses many of these into generic types that compose via DSL (condition + action + event). The collapse is partial: the alias table covers 35 legacy neoorigins: type IDs, and PowerTypes.java still registers 122 power types, including the legacy types that have no DSL equivalent yet.

For existing packs: nothing to change. Legacy type IDs still work, either via the alias table or, for the six legacy types that are still registered (see the deprecation log line), through their own class. On first load of an aliased legacy type, the log prints a one-time [2.0-legacy] power type '<old>' is deprecated, remap to '<new>' (first seen on power '<id>') warning so you know where to migrate when you next touch the pack.

For new / updated packs: author against the 2.0 generic types directly. They’re more expressive, cheaper to maintain, and won’t be deprecated in 3.0.

The source of truth for every alias is src/main/java/com/cyberday1/neoorigins/power/registry/LegacyPowerTypeAliases.java. Each register(old, new, remap) call below corresponds to a row here; if this document drifts from the code, the code wins.


The deprecation log line

Every aliased legacy type emits exactly one warning per boot:

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

Grep your logs for [2.0-legacy] to get the migration punch list. Deprecated types will be removed no earlier than NeoOrigins 4.0. You have time.

An alias fires only when its legacy type ID is no longer a registered power type: LegacyPowerTypeAliases.apply returns the ID unchanged when PowerTypes.get(typeId) finds a registration. The // <type> retired in 2.0 comments in PowerTypes.java mark the types that were removed. Six alias sources are still registered, so their alias is dormant: water_breathing, less_item_use_slowdown, active_dash, active_swap, active_fireball and active_bolt. For these the legacy class parses the JSON exactly as written, with no remap and no [2.0-legacy] line. Their “After” examples below show what the alias will rewrite to once the class is removed. This lets us stage the collapse without breaking in-flight packs.


Collapse table

Grouped by target generic type. Each row reflects the actual remap lambda in LegacyPowerTypeAliases.java. The “After” column shows exactly what the JSON is rewritten to before PowerTypes.get(...) dispatches to the generic type.

→ neoorigins:action_on_event

Fourteen legacy types collapse into this single event-listener.

neoorigins:action_on_kill

Before:

{
  "type": "neoorigins:action_on_kill",
  "action": "restore_health",
  "amount": 4.0
}

After:

{
  "type": "neoorigins:action_on_event",
  "event": "kill",
  "entity_action": { "type": "neoorigins:heal", "amount": 4.0 }
}

action also accepts "restore_hunger" (→ neoorigins:feed { food: <amount> }) and "grant_effect" (→ neoorigins:apply_effect { effect, duration, amplifier }).

neoorigins:action_on_hit_taken

Before:

{
  "type": "neoorigins:action_on_hit_taken",
  "action": "teleport",
  "chance": 0.5,
  "min_damage": 2.0
}

After (chance + min_damage compose neoorigins:chance + neoorigins:if_else):

{
  "type": "neoorigins:action_on_event",
  "event": "hit_taken",
  "entity_action": {
    "type": "neoorigins:if_else",
    "condition": {
      "type": "neoorigins:hit_taken_amount",
      "comparison": ">=",
      "compare_to": 2.0
    },
    "if_action": {
      "type": "neoorigins:chance",
      "chance": 0.5,
      "action": {
        "type": "neoorigins:random_teleport",
        "horizontal_range": 16.0,
        "vertical_range": 8.0
      }
    }
  }
}

action also accepts "ignite_attacker" (→ neoorigins:ignite_attacker { ticks }) and "effect_on_attacker" (→ neoorigins:effect_on_attacker { effect, duration, amplifier }).

neoorigins:thorns_aura

Before:

{ "type": "neoorigins:thorns_aura", "return_ratio": 0.25 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "hit_taken",
  "entity_action": {
    "type": "neoorigins:damage_attacker",
    "amount_ratio": 0.25,
    "source": { "name": "magic" }
  }
}

neoorigins:food_restriction

Both modes now use the same outer shape, an action_on_event listening on food_eaten, dispatching an if_else whose condition checks the food being eaten. Cancelling the event blocks the bite.

item_tag may be a single string or an array (allowed_tags is read as an alternative name for it). The remap prefixes every entry with #, so each entry is looked up as an item tag; with more than one entry the per-entry food_item_in_tag conditions are combined with neoorigins:or.

Blacklist, “these foods are forbidden”:

{
  "type": "neoorigins:food_restriction",
  "mode": "blacklist",
  "item_tag": "minecraft:fishes"
}

becomes

{
  "type": "neoorigins:action_on_event",
  "event": "food_eaten",
  "entity_action": {
    "type": "neoorigins:if_else",
    "condition": {
      "type": "neoorigins:food_item_in_tag",
      "tag": "#minecraft:fishes"
    },
    "if_action": { "type": "neoorigins:cancel_event" }
  }
}

Reads: “if the food is in minecraft:fishes, cancel; otherwise allow.”

Whitelist, “only these foods are allowed”:

{
  "type": "neoorigins:food_restriction",
  "mode": "whitelist",
  "item_tag": "yourpack:edible_for_me"
}

becomes

{
  "type": "neoorigins:action_on_event",
  "event": "food_eaten",
  "entity_action": {
    "type": "neoorigins:if_else",
    "condition": {
      "type": "neoorigins:not",
      "condition": {
        "type": "neoorigins:food_item_in_tag",
        "tag": "#yourpack:edible_for_me"
      }
    },
    "if_action": { "type": "neoorigins:cancel_event" }
  }
}

Reads: “if the food is not in yourpack:edible_for_me, cancel; otherwise allow.”

Notice the only thing that changed between blacklist and whitelist is the condition: the whitelist wraps it in neoorigins:not, and cancel_event stays in if_action. No else_action is written; when the condition is false nothing runs and the bite goes ahead. Hand-written powers can get the same result the other way round, by keeping the plain condition and moving cancel_event to else_action:

"entity_action": {
  "type": "neoorigins:if_else",
  "condition": {
    "type": "neoorigins:food_item_in_tag",
    "tag": "#yourpack:edible_for_me"
  },
  "if_action":   { "type": "neoorigins:nothing" },
  "else_action": { "type": "neoorigins:cancel_event" }
}

⚠️ Common mistake: neoorigins:not is a condition type, not an action type. It wraps a condition; it does not replace if_else. If your power’s entity_action.type is neoorigins:not, the parser doesn’t know what to do with it and the whole power silently no-ops. Always keep if_else (or another action type) at the outer level.

neoorigins:hunger_drain_modifier

Before:

{ "type": "neoorigins:hunger_drain_modifier", "multiplier": 1.3 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_exhaustion",
  "modifier": { "operation": "multiplication", "value": 1.3 }
}

neoorigins:natural_regen_modifier

Before:

{ "type": "neoorigins:natural_regen_modifier", "multiplier": 0.5 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_natural_regen",
  "modifier": { "operation": "multiplication", "value": 0.5 }
}

neoorigins:knockback_modifier

Before:

{ "type": "neoorigins:knockback_modifier", "multiplier": 0.25 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_knockback",
  "modifier": { "operation": "multiplication", "value": 0.25 }
}

neoorigins:longer_potions

Before:

{ "type": "neoorigins:longer_potions", "duration_multiplier": 1.5 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_potion_duration",
  "modifier": { "operation": "multiplication", "value": 1.5 }
}

neoorigins:teleport_range_modifier

Before:

{ "type": "neoorigins:teleport_range_modifier", "multiplier": 2.0 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_teleport_range",
  "modifier": { "operation": "multiplication", "value": 2.0 }
}

neoorigins:more_animal_loot

Before:

{ "type": "neoorigins:more_animal_loot", "multiplier": 2.0 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_harvest_drops",
  "modifier": { "operation": "multiplication", "value": 2.0 }
}

neoorigins:efficient_repairs

Before:

{ "type": "neoorigins:efficient_repairs", "cost_multiplier": 0.5 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_anvil_cost",
  "modifier": { "operation": "multiplication", "value": 0.5 }
}

neoorigins:better_enchanting

Before:

{ "type": "neoorigins:better_enchanting", "bonus_levels": 5 }

After (note: add_base, not multiplication):

{
  "type": "neoorigins:action_on_event",
  "event": "mod_enchant_level",
  "modifier": { "operation": "add_base", "value": 5.0 }
}

neoorigins:better_crafted_food

Before:

{ "type": "neoorigins:better_crafted_food", "saturation_bonus": 0.5 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_crafted_food_saturation",
  "modifier": { "operation": "add_base", "value": 0.5 }
}

neoorigins:better_bone_meal

Before:

{ "type": "neoorigins:better_bone_meal", "extra_applications": 1 }

After:

{
  "type": "neoorigins:action_on_event",
  "event": "mod_bonemeal_extra",
  "modifier": { "operation": "add_base", "value": 1.0 }
}

→ neoorigins:persistent_effect

Collapses status-effect variants into a single list-of-effects shape.

neoorigins:status_effect

Pass-through: the legacy JSON shape already matches persistent_effect’s top-level fallback parse. Just change the type. Toggleable stays true by default (matching AbstractTogglePower semantics).

neoorigins:stacking_status_effects

Effects list passes through verbatim. The remap forces "toggleable": false since persistent_effect defaults to toggleable-on.

Before:

{
  "type": "neoorigins:stacking_status_effects",
  "effects": [
    { "effect": "minecraft:speed", "amplifier": 1 },
    { "effect": "minecraft:strength", "amplifier": 0 }
  ]
}

After:

{
  "type": "neoorigins:persistent_effect",
  "toggleable": false,
  "effects": [
    { "effect": "minecraft:speed", "amplifier": 1 },
    { "effect": "minecraft:strength", "amplifier": 0 }
  ]
}

neoorigins:night_vision

Before:

{ "type": "neoorigins:night_vision" }

After:

{
  "type": "neoorigins:persistent_effect",
  "toggleable": false,
  "effects": [
    {
      "effect": "minecraft:night_vision",
      "amplifier": 0,
      "ambient": true,
      "show_particles": false,
      "show_icon": false
    }
  ]
}

neoorigins:glow

Same effect shape as night_vision but with "effect": "minecraft:glowing" and "show_icon": true. Unlike night_vision, the remap does not write "toggleable": false, so the power keeps persistent_effect’s default of true.

neoorigins:water_breathing

Dormant: water_breathing is still a registered type, so this alias does not fire today.

Before:

{ "type": "neoorigins:water_breathing" }

After (gated by neoorigins:in_water, not toggleable, icon hidden):

{
  "type": "neoorigins:persistent_effect",
  "toggleable": false,
  "condition": { "type": "neoorigins:in_water" },
  "effects": [
    {
      "effect": "minecraft:water_breathing",
      "amplifier": 0,
      "ambient": true,
      "show_particles": false,
      "show_icon": false
    }
  ]
}

→ neoorigins:condition_passive

Tick-based condition + action pairs. Default interval is 20 ticks.

neoorigins:action_over_time

This one was documented before it existed: an older revision of the power reference described the name with nothing behind it. It is a registered alias today, so a pack copied from those docs loads and runs as written. The fields line up 1:1 with condition_passive, so the rename is optional tidying rather than a fix: it points the pack at the real type name.

Before:

{
  "type": "neoorigins:action_over_time",
  "interval": 40,
  "condition": { "type": "neoorigins:in_rain" },
  "entity_action": { "type": "neoorigins:heal", "amount": 1.0 }
}

After:

{
  "type": "neoorigins:condition_passive",
  "interval": 40,
  "condition": { "type": "neoorigins:in_rain" },
  "entity_action": { "type": "neoorigins:heal", "amount": 1.0 }
}

neoorigins:biome_buff

Before:

{
  "type": "neoorigins:biome_buff",
  "biome_tag": "minecraft:is_forest",
  "effect": "minecraft:regeneration",
  "amplifier": 0
}

After:

{
  "type": "neoorigins:condition_passive",
  "condition": { "type": "neoorigins:biome", "tag": "minecraft:is_forest" },
  "entity_action": {
    "type": "neoorigins:apply_effect",
    "effect": "minecraft:regeneration",
    "duration": 300,
    "amplifier": 0
  },
  "interval": 20
}

neoorigins:damage_in_biome

Before:

{
  "type": "neoorigins:damage_in_biome",
  "biome_tag": "minecraft:is_ocean",
  "damage_per_second": 1.0,
  "damage_type": "generic"
}

After:

{
  "type": "neoorigins:condition_passive",
  "condition": { "type": "neoorigins:biome", "tag": "minecraft:is_ocean" },
  "entity_action": {
    "type": "neoorigins:damage",
    "amount": 1.0,
    "source": { "name": "generic" }
  },
  "interval": 20
}

Instead of biome_tag, the legacy JSON may give a biomes array of biome IDs. The remap then writes one { "type": "neoorigins:biome", "biome": "<id>" } condition per entry, combined with neoorigins:or when there is more than one.

neoorigins:damage_in_daylight

Before:

{
  "type": "neoorigins:damage_in_daylight",
  "damage_per_second": 1.0,
  "ignite": false
}

After (composed with neoorigins:and + neoorigins:not):

{
  "type": "neoorigins:condition_passive",
  "condition": {
    "type": "neoorigins:and",
    "conditions": [
      { "type": "neoorigins:exposed_to_sun" },
      { "type": "neoorigins:not", "condition": { "type": "neoorigins:in_water" } }
    ]
  },
  "entity_action": {
    "type": "neoorigins:damage",
    "amount": 1.0,
    "source": { "name": "in_fire" }
  },
  "interval": 20
}

When "ignite": true, a { "type": "neoorigins:set_on_fire", "ticks": <fire_ticks> } action is added (fire_ticks defaults to 40). If damage_per_second is also above 0, both actions run, wrapped in { "type": "neoorigins:and", "actions": [ ... ] }. With damage_per_second at 0 or below only the ignite action is written, and with neither, entity_action is neoorigins:nothing. The condition does not check on_fire, so the damage keeps applying while the player burns.

neoorigins:damage_in_water

multiplier is a scale factor applied on top of damage_per_second, not a synonym: the damage each interval is damage_per_second × multiplier, each defaulting to 1.0 when absent. When that product is 0 or below, the remap writes a neoorigins:nothing action instead of neoorigins:damage, so the player is never hurt for 0.

Before:

{
  "type": "neoorigins:damage_in_water",
  "damage_per_second": 1.0,
  "include_rain": true
}

After (rain included via neoorigins:or):

{
  "type": "neoorigins:condition_passive",
  "condition": {
    "type": "neoorigins:or",
    "conditions": [
      { "type": "neoorigins:in_water" },
      { "type": "neoorigins:in_rain" }
    ]
  },
  "entity_action": {
    "type": "neoorigins:damage",
    "amount": 1.0,
    "source": { "name": "drown" }
  },
  "interval": 20
}

include_rain defaults to true. With "include_rain": false, the condition is just { "type": "neoorigins:in_water" }.

neoorigins:burn_at_health_threshold

Before:

{
  "type": "neoorigins:burn_at_health_threshold",
  "threshold_percent": 0.25,
  "fire_ticks": 60
}

After:

{
  "type": "neoorigins:condition_passive",
  "condition": {
    "type": "neoorigins:relative_health",
    "comparison": "<=",
    "compare_to": 0.25
  },
  "entity_action": { "type": "neoorigins:set_on_fire", "ticks": 60 },
  "interval": 20
}

neoorigins:regen_in_fluid

Before (water):

{
  "type": "neoorigins:regen_in_fluid",
  "fluid": "water",
  "amount_per_second": 1.0
}

After:

{
  "type": "neoorigins:condition_passive",
  "condition": { "type": "neoorigins:in_water" },
  "entity_action": { "type": "neoorigins:heal", "amount": 1.0 },
  "interval": 20
}

For "fluid": "lava", condition is { "type": "neoorigins:submerged_in", "fluid": "minecraft:lava" }.

→ neoorigins:attribute_modifier

Consolidated attribute modifier with optional condition / equipment_condition / location_condition gates (these fields pre-date 2.0; see v1.13 release notes).

neoorigins:less_item_use_slowdown

Dormant: less_item_use_slowdown is still a registered type, so this alias does not fire today.

Before:

{
  "type": "neoorigins:less_item_use_slowdown",
  "speed_multiplier": 0.5,
  "item_type": "any"
}

After:

{
  "type": "neoorigins:attribute_modifier",
  "attribute": "minecraft:movement_speed",
  "amount": 0.5,
  "operation": "add_multiplied_base",
  "condition": { "type": "neoorigins:using_item" }
}

Lossy: the legacy item_type filter ("bow" / "shield") is dropped. The alias applies to any item-use. When item_type is present and not "any", a _migration_note field is written into the rewritten JSON; nothing reads or logs it. If you need the filter, stay on the legacy class until a future DSL verb lands.

→ neoorigins:active_ability

Active-ability DSL collapses eight legacy active types onto entity_action. Four of them (active_dash, active_swap, active_fireball, active_bolt) are still registered types, so their aliases are dormant and the legacy classes keep running.

neoorigins:active_launch

Before:

{ "type": "neoorigins:active_launch", "power": 1.5 }

After:

{
  "type": "neoorigins:active_ability",
  "entity_action": { "type": "neoorigins:add_velocity", "y": 1.5 }
}

neoorigins:active_dash

Before:

{ "type": "neoorigins:active_dash", "strength": 1.2 }

After (dormant alias):

{
  "type": "neoorigins:active_ability",
  "entity_action": {
    "type": "neoorigins:pull_entities",
    "radius": 0,
    "strength": -1.2
  }
}

This rewrite does not reproduce the dash: neoorigins:pull_entities pushes other living entities and never moves the caster, and a radius of 0 only reaches entities overlapping the caster’s hitbox. For a look-direction dash in new JSON, use neoorigins:dash or neoorigins:add_velocity with "space": "local".

neoorigins:repulse

Before:

{ "type": "neoorigins:repulse", "radius": 6.0, "strength": 1.0 }

After:

{
  "type": "neoorigins:active_ability",
  "entity_action": {
    "type": "neoorigins:pull_entities",
    "radius": 6.0,
    "strength": -1.0,
    "include_players": true
  }
}

neoorigins:active_aoe_effect

Before:

{
  "type": "neoorigins:active_aoe_effect",
  "radius": 8.0,
  "effect": "minecraft:weakness",
  "duration": 200,
  "amplifier": 0
}

After:

{
  "type": "neoorigins:active_ability",
  "entity_action": {
    "type": "neoorigins:area_of_effect",
    "radius": 8.0,
    "include_source": false,
    "entity_action": {
      "type": "neoorigins:apply_effect",
      "effect": "minecraft:weakness",
      "duration": 200,
      "amplifier": 0
    }
  }
}

The caster is excluded unless the legacy JSON sets "include_source": true. duration_ticks is read as an alternative to duration.

neoorigins:active_swap

Before:

{ "type": "neoorigins:active_swap", "range": 20.0 }

After (dormant alias; swaps with NEAREST entity in radius; legacy used a look-direction raycast):

{
  "type": "neoorigins:active_ability",
  "entity_action": { "type": "neoorigins:swap_with_entity", "radius": 20.0 }
}

neoorigins:active_fireball

Before:

{ "type": "neoorigins:active_fireball", "speed": 1.5 }

After (dormant alias; lossy, legacy fired 3–4 fireballs with spread; alias fires one):

{
  "type": "neoorigins:active_ability",
  "entity_action": {
    "type": "neoorigins:spawn_projectile",
    "entity_type": "minecraft:small_fireball",
    "speed": 1.5
  },
  "_migration_note": "active_fireball alias shoots a single fireball — legacy fired 3-4 with spread"
}

neoorigins:active_bolt

Before:

{ "type": "neoorigins:active_bolt", "speed": 1.2 }

After (dormant alias):

{
  "type": "neoorigins:active_ability",
  "entity_action": {
    "type": "neoorigins:spawn_projectile",
    "entity_type": "minecraft:wind_charge",
    "speed": 1.2
  }
}

neoorigins:healing_mist

Before:

{
  "type": "neoorigins:healing_mist",
  "heal_amount": 6.0,
  "radius": 8.0,
  "heal_self": true
}

After:

{
  "type": "neoorigins:active_ability",
  "entity_action": {
    "type": "neoorigins:area_of_effect",
    "radius": 8.0,
    "include_source": true,
    "entity_action": { "type": "neoorigins:heal", "amount": 6.0 }
  }
}

Cross-mod compat aliases

Foreign-namespace equivalents for packs extracted from Apugli (abandoned at 2.11.0+1.20.4) and legacy Apoli. The loader first rewrites apoli: and apugli: power types to origins: (OriginsFormatDetector.canonicalizePowerType), so these reach the origins: / apace: translator. When the translator has no case for the rewritten ID, PowerDataManager.resolvePowerType restores the authored ID and hands it to the alias table below.

apugli:edible_item / apoli:edible_item → neoorigins:edible_item

After canonicalization these match the translator’s origins:edible_item case, which produces the same shape as the alias entries registered for them. The rewrite hoists singular item → items[], singular tag → tags[], and flattens food_component.{nutrition, saturation_modifier, always_edible} up to the root (food_component.saturation is accepted when saturation_modifier is absent).

Before:

{
  "type": "apugli:edible_item",
  "item": "minecraft:rotten_flesh",
  "food_component": {
    "nutrition": 4,
    "saturation_modifier": 0.2,
    "always_edible": true
  }
}

After:

{
  "type": "neoorigins:edible_item",
  "items": ["minecraft:rotten_flesh"],
  "nutrition": 4,
  "saturation": 0.2,
  "always_edible": true
}

Apugli-only fields effect, action, return_stack are dropped (same policy as the origins/apace translator).

apugli:action_on_jump → action_on_event { event: jump }

Thin event-key injection; entity_action passes through verbatim.

apugli:action_on_target_death → action_on_event { event: kill }

Thin event-key injection; entity_action passes through verbatim.


Power types NOT retired

These legacy types kept their standalone classes because the DSL can’t express them yet (too stateful, not an event trigger, or no matching verb exists). They still work, just aren’t collapsed.

Active abilities (stateful / specialised):

  • neoorigins:active_teleport: no look-direction teleport verb
  • neoorigins:active_recall: stateful saved position
  • neoorigins:active_place_block: no raycast-and-place verb
  • neoorigins:shadow_orb: stateful orb with tick loop
  • neoorigins:ground_slam: no alias registered
  • neoorigins:tidal_wave: no alias registered
  • neoorigins:active_phase: movement state toggle, not an active ability

Aliased but still registered (dormant aliases, see the deprecation log line):

  • neoorigins:water_breathing, neoorigins:less_item_use_slowdown, neoorigins:active_dash, neoorigins:active_swap, neoorigins:active_fireball, neoorigins:active_bolt

Non-tick-based condition interceptors:

  • neoorigins:mobs_ignore_player: LivingChangeTargetEvent interceptor
  • neoorigins:no_mob_spawns_nearby: MobSpawnEvent.FinalizeSpawn interceptor
  • neoorigins:item_magnetism: item-entity pull, no DSL verb yet
  • neoorigins:breath_in_fluid: air-supply drain, no DSL verb yet

Attribute-modifier candidates held back:

  • neoorigins:break_speed_modifier: applied through PlayerEvent.BreakSpeed on both sides (BreakSpeedModifierEvents) so its block_tag filter sees the target block
  • neoorigins:underwater_mining_speed: modifies the submerged_mining_speed attribute
  • neoorigins:no_slowdown: cancels block-induced slowdown (cobweb, sweet berry bush, powder snow, soul sand, honey) through mixins, optionally filtered by block_tag

Persistent-effect candidates held back:

  • neoorigins:effect_immunity: migrates under a different generic in a later phase

See the carve-out comments in LegacyPowerTypeAliases.registerActiveAbilityAliases, registerConditionPassiveAliases, registerAttributeModifierAliases, and registerPersistentEffectAliases for the authoritative list. Phase 7+ may add raycast / cone / mob-AoE verbs and shrink this list further.

neoorigins:gravity_well was removed in 2.0 with no alias: it is neither registered nor in the alias table, so a power that still uses it logs Unknown power type and does not load.


Verb namespace migration: origins: / apace: → neoorigins:

In 2.0.0 the canonical DSL verb namespace is neoorigins:. The legacy origins: and apace: prefixes still parse (every verb your pack uses today will keep working). The action and condition parsers rewrite any prefix other than neoorigins: (including apoli: and apugli:) to neoorigins:<name>, and emit a one-shot [2.0-legacy] warning per distinct verb at first use:

[2.0-legacy] DSL verb 'origins:damage' is deprecated, use 'neoorigins:damage'

Grep your logs for [2.0-legacy] to get the migration punch list. Verbs under the legacy prefixes will be removed no earlier than NeoOrigins 3.0. You have the full 2.x lifecycle to migrate.

Why

Pre-2.0, the parser was an Apoli-compat layer. Verb names lived under origins: because that’s what upstream Apoli used. When 2.0 introduced its own power types (neoorigins:condition_passive, neoorigins:modify_damage, …) the DSL verbs they composed with stayed on origins:*, producing the awkward mix of namespaces inside a single power JSON:

// Pre-2.0 style — mixed namespaces
{
  "type": "neoorigins:condition_passive",
  "condition": { "type": "origins:biome", "tag": "minecraft:is_nether" },
  "entity_action": { "type": "origins:damage", "amount": 1.0 },
  "interval": 20
}

The canonicalization fixes this. Power types, condition verbs, and action verbs all live under the same namespace:

// 2.0.0 canonical
{
  "type": "neoorigins:condition_passive",
  "condition": { "type": "neoorigins:biome", "tag": "minecraft:is_nether" },
  "entity_action": { "type": "neoorigins:damage", "amount": 1.0 },
  "interval": 20
}

How to migrate

For a quick pass, run a find-replace over your pack’s JSON:

# On every power JSON:
sed -i 's/"type": *"origins:/"type": "neoorigins:/g' path/to/powers/*.json
sed -i 's/"type": *"apace:/"type": "neoorigins:/g' path/to/powers/*.json

The verb names are identical: only the namespace changes. No field shapes were changed as part of this canonicalization.

Bare verb names ("type": "and") still work and are auto-prefixed; the autoprefix target moved from origins: → neoorigins: for the 2.0 release. If your pack relies on bare names and needed them resolved as origins:* for some reason, write them out explicitly.

Verbs that were already neoorigins:*

A dozen or so verbs introduced fresh in 2.0 were already namespaced correctly: neoorigins:in_set, neoorigins:no_minions_alive, neoorigins:hit_taken_amount, neoorigins:food_item_in_tag, neoorigins:toggle, neoorigins:add_to_set, neoorigins:remove_from_set, neoorigins:chain_to_nearest, neoorigins:pull_entities, neoorigins:swap_with_entity, neoorigins:teleport_to_marker, neoorigins:damage_attacker, neoorigins:ignite_attacker, neoorigins:effect_on_attacker, neoorigins:random_teleport, neoorigins:cancel_event, neoorigins:spawn_projectile.

These were unchanged.


Lossy translations: aliases that drop fields

A handful of aliases simplify the legacy behaviour because no 2.0 DSL verb matches the legacy 1:1. All three below are dormant today: the legacy classes are still registered, so a pack using these types gets the full legacy behaviour. The table is what changes once a class is removed and the alias kicks in.

Legacy type What’s dropped Workaround / roadmap
active_dash The dash itself: the alias writes a neoorigins:pull_entities push with radius 0, which moves other entities, not the caster. New JSON can dash along the look vector with neoorigins:dash or neoorigins:add_velocity with "space": "local".
active_fireball Multi-shot spread: legacy fired 3–4 small fireballs with a random spread cone. The alias shoots one. No multi-shot DSL verb yet. Legacy class still registered; keep it for shotgun behaviour.
less_item_use_slowdown Item-type filter (item_type: bow / shield / any). Alias applies to any item-use. No item-type condition verb yet. Packs filtering by tool keep the legacy class.

active_fireball (always) and less_item_use_slowdown (when filtering by item type) also write a _migration_note field into the rewritten JSON. Nothing reads or logs that field; the only runtime log entry is the one-time [2.0-legacy] line.


DSL gaps worth knowing about

Runtime hooks and verb shapes that don’t exist in 2.0 yet, flagged in LegacyPowerTypeAliases.java as carve-outs. Each one blocks one or more alias collapses until it lands.

Missing verb / hook Blocks Notes
Multi-shot projectile active_fireball shotgun, future spray powers Either loop spawn_projectile × N with a spread seed, or a dedicated spawn_projectile_spread verb.
Raycast-and-place block active_place_block Needs a block-placement action with a raycast source.
Item-type condition less_item_use_slowdown filter Predicate on the item stack the player is currently using.
Air-supply modifier action breath_in_fluid Either a tick-driven attribute or a dedicated air-drain action.
Item-entity pull action item_magnetism pull_entities only moves living entities; needs an item-entity variant.

Some verbs the carve-out comments list as missing now exist, but the aliases have not been updated to use them: add_velocity takes a space field ("local" is relative to the look direction) and neoorigins:dash pushes along the look vector; area_of_effect accepts "shape": { "type": "cone", "angle": <degrees> } and also reaches mobs for verbs that support mob targets (such as apply_effect, damage and heal).


Questions

If you hit a legacy type not documented here, check LegacyPowerTypeAliases.java. It’s the source of truth. Or open an issue.