promotional bannermobile promotional banner

Overgrown's Apoli

A data-driven power system that's build for custom abilities, mechanics, and entire origin systems from JSON alone.
Back to Files

Apoli (1.83.0)

File nameapoli-1.83.0.jar
Uploader
OvergrownMCOvergrownMC
Uploaded
Sep 14, 2026
Downloads
61
Size
29.3 MB
Mod Loaders
Fabric
File ID
8882907
Type
R
Release
Supported game versions
  • 1.20.1

Curse Maven Snippet

Fabric

modImplementation "curse.maven:apoli-1631826:8882907"

Learn more about Curse Maven

What's new

1.83.0

Added

  • own_key on apoli:power_storage. A stored power is really granted, so it also answers the keybind its author gave it. For a satchel of spells that is the point; for stealing an ability it is not, because the stolen power arrives on whatever key its author picked — usually a key the thief already uses — so one press fires both, and the apoli:run_stored_power you wired up becomes a redundant second route to the same ability. With own_key: false the stored power stays fully granted — resources, cooldowns, HUD bars, apoli:multiple sub-powers, all of it — but its own key does nothing and apoli:run_stored_power is the only way to fire it. Defaults to true, so nothing changes for a pack that does not set it.

    It follows a stored apoli:multiple down into its sub-powers, so a stolen bundle goes quiet as a unit, and it silences only what came out of that storage: a power the holder also has from their origin keeps its key, and so does their own power on the same key. That is the difference from apoli:prevent_key_press, which blocks a key for everything the holder has.

Fixed

  • A power storage granted by command deleted itself. /apoli:power grant <targets> <power> with no explicit source uses the power's own id as its source, which put an apoli:power_storage into its own source list — and the storage then revoked itself the next time it reconciled. Granting a storage straight to a player, which is the obvious way to try the feature out, produced a power that vanished and a storage that appeared to do nothing. A storage declared inside an apoli:multiple is sourced by the bundle and was never affected.

1.82.0

Added

  • Dev mode explains power storage. Every way apoli:store_power can quietly do nothing now prints a line under /apoli:dev_mode, naming the storage and the power: stored, or refused because the target holds no storage at all, because no storage has the id you named, because from_held found nothing matching on the actor, because the power carries none of the storage's tags, because it is already stored, or because every slot is full. from_held also names the entity it read the powers from, since storing into the wrong side of a bi-entity action is the usual reason nothing happens.

    apoli:run_stored_power does the same for a cast that does nothing: whether the storage was empty, whether power / tags / index matched nothing, or whether something matched and refused to fire — the last one being the case worth naming, since a stored power only answers that action if it is key-driven, and its own condition, cooldown and apoli:prevent_key_press all still apply.

Fixed

  • apoli:stored_power counted powers that no longer exist. An entry whose power had been deleted, renamed, or had its data pack disabled still counted toward the condition, so apoli:stored_power and apoli:resource read against the same storage could disagree by exactly the number of dead entries — and a storage that looked non-empty to the condition could not be emptied by anything. Both now skip them.

1.81.1

Fixed

  • Bedrock models were flat on Fabric 1.20.1. BedrockModelParser.vector() on that tree read only the first component of a pivot, origin, size or cube rotation array, so every one of them collapsed to (x, 0, 0) and apoli:custom_model_render in geometry mode baked zero-height slivers stacked on the floor. It read all three on both 1.21.1 trees; the file is now identical across all three. Animation parsing was never affected.

1.81.0

Changed

  • grant on apoli:power_storage now defaults to true. It shipped opt-in in 1.80.2, and that was the wrong default: without it a stored power is only as capable as a single key press can express, so anything built out of several pieces — the usual shape of an ability, one key power plus the resources, timers and cooldown behind it — fired into a void. Every branch reading a resource took the wrong path and the ability appeared to do nothing. grant: false is still there for a storage that is a list rather than a loadout, such as a spell book cast entirely through apoli:run_stored_power.

1.80.3

Fixed

  • A storage could store itself, and then hold nothing useful. tags is read twice from the same JSON key: once as the power's own tags, and once by apoli:power_storage as its accept-filter. So "tags": "contractable" on a storage also tagged that storage contractable, which made it a candidate for its own filter — apoli:store_power with from_held saw it in the holder's power list, and with slots: 1 and replace_oldest it could evict the real ability and store the storage instead. The result was a power that applied and did nothing, and which ability won varied run to run. A apoli:power_storage can no longer be stored in a storage.

1.80.2

Added

  • grant on apoli:power_storage. A stored power is inert by design — it does not tick, holds no resources and draws no HUD, which is right for a spell book and wrong for anything assembled from more than one piece. An apoli:multiple is usually one key ability plus the resources, timers and cooldown behind it, so stored inertly the ability fired and everything underneath it was missing. With grant: true a power is really granted while it sits in the storage, with the storage power as its source: resources exist, action_over_time sub-powers tick, HUD bars draw, and a stored multiple grants all of its sub-powers. Evicting it, clearing the storage or losing the storage power revokes everything it brought.

1.80.1

Fixed

  • tags on an apoli:multiple was silently discarded. tags is a top-level power field, but it was missing from the bundle's reserved-field list, so the loader treated it as a would-be sub-power, found it wasn't an object, and dropped it — leaving the bundle with no tags at all. Anything that selects powers by tag therefore skipped every apoli:multiple: most visibly apoli:store_power with from_held and a tags filter, which would happily store a tagged plain power and never a tagged bundle.
  • A field on an apoli:multiple that is neither reserved nor an object is now logged by name at load instead of being ignored without a word. That is what made the bug above invisible.
  • A stored power whose JSON no longer exists could never be cleared. It kept occupying a slot, kept counting toward the storage's resource value, and there was no command that could remove it. Such an entry is now ignored when reading the storage, never fired, and dropped the next time something is stored.

Added

  • apoli:power_storage is now a writable resource, so /apoli:resource set <targets> <storage> 0 empties a storage. A smaller number keeps that many of the most recently stored powers.

1.80.0

Added

  • Grief-log attribution for Ledger and Indexor. When a power edits the world, the log entry is labelled with the power — and, with Origins installed, the origin it came from — instead of @explosion or @player. A Ledger entry reads @blaze/fireball (Steve), and the label is a real Ledger source, so /ledger search source:blaze/fireball and /ledger rollback source:blaze/fireball both work on it. Indexor entries that could only be blamed on Environment get the acting player and the power filled in instead. Attribution follows apoli:delay and apoli:loop chains, and nested powers attribute to the innermost one running. Neither integration touches the block-entity NBT either mod replays during a rollback. No supported logger installed means no cost at all — the mechanism compiles out.
  • apoli:sound_playing (entity condition; aliases playing_sound, hearing_sound) — true when a matching sound started near the entity within duration ticks. Fields: sound, category, duration, range (defaults to the sound's own audible radius). Only sounds a loaded power actually names are tracked, so a pack that never uses it pays nothing.
  • gui_scale_lock on apoli:overlay — draw an overlay at a fixed GUI scale instead of the viewer's, so a reticle or frame is the same physical size on every client. 0 (the default) follows the viewer's setting.
  • actions array on apoli:action_over_time — several actions on their own intervals inside one power, all driven by a single evaluation of the power's condition. Each step takes interval, entity_action, and optional onset_delay and condition. Three effects on three beats now cost one condition check per interval instead of three.

Changed

  • strength, red, green and blue on apoli:overlay accept expressions, so a tint can follow a resource. A plain number is folded at load and costs nothing to draw.

1.79.0

Added

  • load_condition — a top-level power field checked while the data pack loads, before the power is parsed at all. This is what makes one pack work across two game versions: a branch whose fields this version's codec would reject is dropped before it can fail. A runtime condition cannot do this, because the power has to parse first.
  • apoli:mod_loaded (mod, mods, version, comparison; alias mod_installed) and apoli:minecraft_version (version, data_version, comparison; aliases game_version, mc_version), registered on all condition registries — entity, bi-entity, item, block, fluid, biome, damage, and the new load-time one.
  • A static condition registry for load_condition, holding all_of, any_of, constant, nothing and the two new types. Anything else in a gate is a clean parse error naming the power, rather than a condition that silently answers wrong.

Notes

  • Version comparison uses one shared comparator on every loader and version, so the same JSON answers identically everywhere. Snapshots do not order sensibly — use data_version for those.
  • Requires Origins 1.35.0 or newer if you use both.

1.78.1

Fixed

  • Server crash when two powers shared a key-sequence prefix and one of them ended on a held step — pressing two keys on the same tick could run the matcher past the end of the sequence.

1.78.0

Added

  • Key-sequence steps. Entries in key_sequence can now be objects instead of bare key names, with key, taps (1–16), hold (ticks) and max_gap (ticks). "Tap it three times and hold the last one" is a single entry. A bare string behaves exactly as it always has.
  • grace on apoli:key_pressed (0–40 ticks) — keeps the condition passing for a moment after the key is released, for keyboards that drop a keystroke under rollover.

Fixed

  • A one-tick keyboard dropout no longer reads as a release. The transport stays raw and the smoothing is applied per consumer, so action_on_key_sequence still sees real press and release edges and double-taps keep working.
  • key_sequence keys are now watched even when they are not also listed in keys — a combo built only out of sequence keys could never fire.
  • The documented nested form of a keys entry ({"key": {"key": "key.x"}}) parses again.

1.77.0

Fixed

  • action_on_land never fired for anyone with take_fall_damage: false. The immunity was clearing fall distance every tick, and fall distance is the only signal that a landing happened. The immunity is now applied after the landing hook instead.

Changed

  • /apoli:dev_mode traces held-key changes (held keys +key.x -key.y), so you can tell "the server saw a release" from "the power logic is wrong".

1.76.0

Fixed

  • bientity_condition on apoli:modify_tick_rate did nothing once affect_chunks was on. A rejected entity simply got no tick state and fell back to the chunk's rate. Rejected entities are now written an explicitly-normal rate. Setting bientity_condition on a target it cannot filter now logs the power at load instead of sitting silently inert.
  • A stored apoli:multiple never ran. Neither key dispatch nor the stored-power tick had a branch for a bundle, so a stored multiple forwarded to none of its children.
  • apoli:power_storage did not always clear when the power was removed. Deleting, renaming or disabling the storage power's JSON and then revoking it left the stored list behind, so the next grant of that id inherited the old contents.
  • drop_on_death on apoli:power_storage was declared and read by nothing; it now clears the storage.
  • Black custom particles. Not a render bug — particle.fsh is straight-alpha, and a soft-edged PNG whose feathered pixels lost their colour legitimately paints black. Every vanilla particle texture is binary-alpha, so nothing in the game exercised that path.

Added

  • alpha_bleed on apoli:custom particles — dilates the opaque colour outward into the semi-transparent fringe at load, which fixes the black ring above without touching the source file. Opt-in, because full dilation would also flatten a deliberately coloured ramp.

1.75.0

Added

  • apoli:six_arms built-in player model, in the same idiom as apoli:four_arms — three pairs fanning outward, every vanilla arm state (swing, item, bow, sleeping, elytra) reaching all six arms. apoli:four_arms is unchanged.

Fixed

  • NeoForge: speech-to-action crashed on startup with Could not initialize class com.sun.jna.Native. Minecraft ships its own JNA and was winning the resource lookup, so Apoli's JNA loaded the wrong native library. The isolated loader is now child-first for resources as well as classes.

1.74.0

Fixed

  • No fall damage for anyone, anywhere. apoli:modify_bounciness was cancelling vanilla's fall-damage call for every entity and every block, whether or not any power matched. Shipped in the same commit that added the power type.
  • Performance, same area: the bounciness hook ran a block lookup and allocated a context for every living entity in every loaded chunk, every tick they were on the ground — not just on landing.

Added

  • apoli:power_storage — holds power ids without granting them, in the holder's own data. Deliberately ungranted, so a stored action_over_time does not quietly run. Readable as a resource, so HUD bars and $(key) macros work on it for free.
  • apoli:run_stored_power (entity action) — fires a stored power with a key of your choosing. The power does not need to be held or granted.
  • apoli:store_power (entity and bi-entity) — operation: add | remove | clear, and from_held to take the ids straight off the actor's held powers.
  • apoli:stored_power (entity condition) — a count condition, so "has X", "is full" and "is empty" are all one type.
  • apoli:same_equipped_item (bi-entity condition) — compare_id, compare_count and compare_tag are independent switches, slots accept one or a list, and multi-slot means any-against-any.
Type
Name
Game Versions
Mod Loaders
Size
Downloads
Uploaded