## 1.124.0
### Before you update
- `apoli:sneaky` and `apoli:share_effects` are gone, and a power that uses either one no longer loads. Both can be built from other powers.
### Removed: `apoli:sneaky` and `apoli:share_effects`
`apoli:sneaky` and `apoli:share_effects` were both compositions of powers Apoli already has. For Sneaky, `apoli:scale` on `apoli:visibility` makes mobs notice the player from 80% of the usual distance. `apoli:modify_label_render` fades the nameplate and hides it past 32 blocks with an `apoli:distance` condition. `apoli:replace_sound_emission` mutes step sounds while `apoli:on_block` holds, and `apoli:prevent_game_event` stops the `minecraft:step` vibration. Foxes and the "Time Sneaked" statistic read vanilla's sneak flag directly, so those are the two things the composition doesn't cover.
For Share Effects, an `apoli:action_over_time` that runs an `apoli:area_of_effect` with `apoli:transfer_effects` does the same job. This one gives the holder's tamed pets within 8 blocks a copy of every effect the holder has, twice a second:
```json
{
"type": "apoli:action_over_time",
"interval": 10,
"entity_action": {
"type": "apoli:area_of_effect",
"radius": {
"x": 8,
"y": 2,
"z": 8
},
"bientity_condition": {
"type": "apoli:all_of",
"conditions": [
{
"type": "apoli:owner"
},
{
"type": "apoli:target_condition",
"condition": {
"type": "apoli:tamed"
}
}
]
},
"bientity_action": {
"type": "apoli:transfer_effects",
"mode": "give",
"copy": true
}
}
}
```
### Performance: `apoli:attribute`
Attribute powers now use less memory and CPU. They check their modifiers every tick. That is how the modifiers come back after a login or a reload, and how they follow a condition. The check used to build a lookup key, a string and a modifier id for every modifier on every holder, every tick, and on 1.20.1 it also hashed the id with MD5. Apoli now works out the ids once per power and reads the value it applies straight from the entity's own attribute. A power with four modifiers went from 1.1 KB of garbage and 1.1 µs per tick to none and 0.13 µs on 1.21.1, and from 2.8 KB and 1.7 µs to 64 bytes and 0.09 µs on 1.20.1. Apoli tests the condition once per power instead of once per modifier, and it no longer scans every attribute modifier in the world when an entity is removed.
## 1.123.0
### Before you update
- The voice chat and speech powers left Apoli for a new addon, Birdsong: `action_on_speak`, `action_on_reply`, `action_on_speech`, `modify_speaking_range`, the five `voice_*` conditions and the `voice_*` expression variables. Install Birdsong to keep them working. Without it, packs that use the `apoli:` ids still load, but those powers do nothing, the conditions read `false` and the variables read `0`.
- `loading_priority` now decides between packs for global power sets and origins, so a lower pack with a higher priority can win over the top pack.
- Speech settings move from `apoli-client.json` to Birdsong's `birdsong-client.json` the first time Birdsong starts.
### Added: JSONC and JSON5 data files
Every Apoli and Origins data folder now also accepts `.jsonc` files, which allow `//` and `/* */` comments and a comma after the last item. These folders also accept `.json5` files, which allow names without quotes, single quotes, hexadecimal numbers, `.5` and `5.`, `Infinity` and `NaN`, along with the JSONC features. That covers powers, macros, global power sets, custom status effects, keybinds, skill trees, origins, layers and badges. Apoli reads them with its own parser, so it doesn't need Quilt's parser library. Thanks to FLD for the original pull request.
```json5
{
type: 'apoli:swim_speed',
name: 'Strong Swimmer',
modifier: {
operation: 'multiply_total_multiplicative',
value: .5,
},
}
```
Apoli skips a file that doesn't parse, and the log names its pack, the line, the column and the path to the bad value. When a plain `.json` file has a comment or trailing comma, the error names the extension that would accept it. Text after the last bracket only logs a warning. Two files with the same id in one pack, such as `swim.json` and `swim.json5`, log a warning, and the `.json` file wins. Between packs, the higher pack wins whatever the extension.
### Fixed: `loading_priority` on global power sets
Global power sets used to read only the top pack's file, so `loading_priority` did nothing. It now decides which pack's version wins, the same way custom status effects already did.
### Changed: voice chat and speech moved to Birdsong
Birdsong registers the voice types as `birdsong:` ids and keeps every `apoli:` id as an alias, so existing packs work unchanged after you install it. The Speech to Action key, the speech settings and `/apoli:speech` moved with it. Speech recognition no longer ships inside Apoli, and Apoli's jar went from 31.7 MB to 4.3 MB.
### Changed: loaders
This release is built against Fabric Loader 0.20.1, Fabric API 0.116.17 on 1.21.1 and 0.92.12 on 1.20.1, and NeoForge 21.1.265.
### Performance: faster data loading
The new parser reads data files about twice as fast as Gson did.
## 1.122.0
### Added: play a chosen sound file
`apoli:play_sound` takes an `index`: which of the sound event's sound files to play, counting from 0 in the order `sounds.json` lists them. Without an index, a random one plays, as before, and an index past the end of the list also plays a random one. The index can be an expression too.
```json
{
"type": "apoli:play_sound",
"sound": "minecraft:entity.pig.ambient",
"index": 2
}
```
Each player's game picks the file from its own copy of the sound, so a resource pack that changes the list changes which file an index plays for that player.
## 1.121.0
### Before you update
- `apoli:stacking_status_effect` now lets the count sink to `min_stacks` while the power is inactive, and only applies effects while it is active. A pack with a negative `min_stacks` gets the delay it asked for: Elytrian's claustrophobia waits about ten seconds under a low ceiling.
- When `apoli:pose` forces a player into `sitting` or `sliding`, they get a lower hitbox and camera.
### Fixed: Pose
`apoli:pose` changed the hitbox for some poses but left the model as it was. Every pose now shows on players and on mobs that use the humanoid model, such as zombies and skeletons.
- `crouching` crouches the player's own model, and the player moves at sneaking speed.
- `sitting`, `sliding`, `shooting`, `inhaling`, `long_jumping`, `croaking`, `using_tongue`, `roaring`, `sniffing`, `emerging` and `digging` each have a pose of their own. The Pose page in the Handbook describes them.
- `dying` falls over sideways and stays down, and `fall_flying` glides fully.
- `sitting` lowers a player's hitbox to 1.2 blocks with the eyes at 1.0, and `sliding` to 0.9 with the eyes at 0.6, so a sliding player fits under a one-block gap.
`sliding`, `shooting` and `inhaling` only exist on 1.21.1.
### Fixed: stacking status effect
A negative `min_stacks` delayed nothing, so Elytrian got Weakness and Slowness the moment it walked under a low ceiling. The count now has to climb back above 0 before the effects start, and a newly granted power starts at `min_stacks` when it is negative.
### Fixed: sounds from resource packs
`apoli:play_sound`, the `sound` of `apoli:fire_projectile` and the `consume_sound` of `apoli:edible_item` played nothing for a sound that only a resource pack defines. They play it now, for every player who has the pack.
### Added: fallback textures for HUD bars
`hud_render` takes a `fallback`: a texture, an object with its own `sprite_location`, `bar_index` and `icon_index`, or a list of them. Players who don't have the resource pack behind `sprite_location` see the first fallback that exists instead of the missing texture.
```json
"hud_render": {
"sprite_location": "example:textures/gui/mana_bar.png",
"fallback": {
"sprite_location": "apoli:textures/gui/resource_bar.png",
"bar_index": 3,
"icon_index": 2
}
}
```
### Added: spawn offsets
`apoli:spawn_entity` (entity and block action), `apoli:summon_clone`, `apoli:summon_minion` and `apoli:spawn_effect_cloud` take an `offset`. The entity actions also take a `space`, so `"local"` places the entity relative to where the caster is looking.
```json
{
"type": "apoli:spawn_entity",
"entity_type": "minecraft:armor_stand",
"offset": { "z": 2 },
"space": "local"
}
```
## 1.120.0
### Added: Modify Player Spawn fields
`apoli:modify_player_spawn` has four new fields, contributed by FLDebug10.
| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `biome_condition` | Biome condition | optional | Spawn in the nearest biome that passes it. With `biome` set too, either one counts. |
| `structure_tag` | Identifier | optional | Spawn at the nearest structure in this tag. With `structure` set too, either one counts. |
| `block_condition` | Block condition | optional | Only accept a spot where the block at the player's feet passes it. Wrap it in `apoli:offset` with `"y": -1` to test the floor. |
| `search_radius` | Integer | `32` | How many blocks the safe-spot search reaches, from 1 to 64. A value outside that range is clamped with a warning. |
```json
{
"type": "apoli:modify_player_spawn",
"dimension": "minecraft:overworld",
"structure_tag": "minecraft:village",
"block_condition": {
"type": "apoli:offset",
"y": -1,
"condition": {
"type": "apoli:block",
"block": "minecraft:grass_block"
}
}
}
```
### Performance: Modify Player Spawn
The power no longer keeps a 23×23-chunk area loaded around every spawn point it finds; those chunks stayed loaded and ticking for as long as the server ran. Apoli remembers biome and structure searches until the data pack reloads, so only the first player with the power waits for them. It does not repeat a safe-spot search that finds nothing for five minutes.
### Fixed: Modify Player Spawn
- On 1.20.1, choosing an origin with this power, such as Blazeborn, only moved the respawn point and didn't teleport the player. It teleports them now.
- `block_condition` checked the neighbouring block at negative coordinates.
- The safe-spot search could put a player on top of the Nether's bedrock roof.
### Fixed: reach on 1.20.1
Reach from other mods, such as Reach Entity Attributes (which many 1.20.1 mods bundle), was overridden on the server, so a block mined from beyond vanilla reach broke on the client, reappeared and dropped nothing. That reach now stacks with Apoli's. Apoli's own reach also works for using and placing blocks more than about 8 blocks away.
### Fixed: ropes
- A rope could stay on screen after you left a server while attached, even in later sessions.
- Apoli hides a rope whose end is out of sight instead of freezing it in place.
- Players who join, respawn or change dimension now see ropes that already exist.
### Fixed: keybinds
Origins' and Apoli's ability keys reset to their defaults whenever options were saved outside a world, for example after changing a setting on the title screen. Bindings now survive. On NeoForge, Apoli also restores a binding with a modifier such as Shift+G correctly.
Optional dependencies
ImmediatelyFast
Any compatible version
Icarus
Any compatible version
[EMF] Entity Model Features
Any compatible version
Pufferfish's Skills
Any compatible version
Accessories
Any compatible version
Curios API
Any compatible version
Sable
Any compatible version
Embeddium (Rubidium) Extra
Any compatible version
Rubidium
Any compatible version
Figura
Any compatible version
woodwalkers
Any compatible version
Sodium Extra
Any compatible version
Hardcore Revival
Any compatible version
Simple Voice Chat
Any compatible version
3D Skin Layers
Any compatible version
Sodium
Any compatible version
[ETF] Entity Texture Features
Any compatible version
LambDynamicLights - Dynamic Lights
Any compatible version
GraveStone Mod
Any compatible version
Ears
Any compatible version
Lithium
Any compatible version
You're in Grave Danger
Any compatible version
KubeJS
Any compatible version
Not Enough Recipe Book [NERB]
Any compatible version
Gravestones
Any compatible version
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }