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:notis a condition type, not an action type. It wraps a condition; it does not replaceif_else. If your power’sentity_action.typeisneoorigins:not, the parser doesn’t know what to do with it and the whole power silently no-ops. Always keepif_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
multiplieris a scale factor applied on top ofdamage_per_second, not a synonym: the damage each interval isdamage_per_second × multiplier, each defaulting to1.0when absent. When that product is 0 or below, the remap writes aneoorigins:nothingaction instead ofneoorigins: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 verbneoorigins:active_recall: stateful saved positionneoorigins:active_place_block: no raycast-and-place verbneoorigins:shadow_orb: stateful orb with tick loopneoorigins:ground_slam: no alias registeredneoorigins:tidal_wave: no alias registeredneoorigins: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:LivingChangeTargetEventinterceptorneoorigins:no_mob_spawns_nearby:MobSpawnEvent.FinalizeSpawninterceptorneoorigins:item_magnetism: item-entity pull, no DSL verb yetneoorigins:breath_in_fluid: air-supply drain, no DSL verb yet
Attribute-modifier candidates held back:
neoorigins:break_speed_modifier: applied throughPlayerEvent.BreakSpeedon both sides (BreakSpeedModifierEvents) so itsblock_tagfilter sees the target blockneoorigins:underwater_mining_speed: modifies thesubmerged_mining_speedattributeneoorigins:no_slowdown: cancels block-induced slowdown (cobweb, sweet berry bush, powder snow, soul sand, honey) through mixins, optionally filtered byblock_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.