Handbook · Minecraft 1.20.1 · Fabric
Too Many Attributes
Too Many Attributes (TMA) is a Fabric mod for Minecraft 1.20.1 that adds new building blocks to Origins datapacks. Everything it adds is used from power JSON with "type": "too-many-attributes:<id>", the same way as Origins' own origins:<id> types.
What it adds:
- 41 entity conditions for things Origins can't check: attack cooldown, recent damage and damage blocked, knockback, crouch depth, sprint time, glide ratio, friction, light level at the eyes, moon phase, sleeping, redstone power, vanilla statistics, and more.
- A block condition for redstone signal strength (
redstone_power), usable wherever Origins checks a block. - Actions Origins lacks: turn an entity's view (
set_rotation,look_at), fake block-crack animations (play_block_crack), item and vibration cooldowns, phantom mobs and fake players only chosen players can see, which powers can act on (spawn_phantom,phantom_action), and disguises that make an entity look like another type (disguise). - Signals: one power announces something by name (
emit_signal) and unrelated powers react (action_on_signal), so separately written origins can work together. - Fake blocks: show chosen players a different block at a position, without changing the world (
set_fake_block, thefake_blockspower,/tma fakeblock). - Power tags: group powers in one datapack file (wildcards included), then filter on them with
has_power_tag, without touching the powers themselves. - 45 read-only attributes with the same values, readable with
/attributeand Origins'origins:attributecondition, with no power needed. - A settable
dragattribute on every living entity, a direct stand-in for vanilla's fall-drag constant, without touching gravity or jump height. - Variables and lists. Typed per-player data (
int,double,String, …) that you change and check with Java expressions. You can use it in any Origins field and in commands, and it can read scoreboards and the items in a power inventory. - A global store. Typed values and lists addressed by a key instead of an entity, saved with the world, with optional locking and automatic cleanup of keys tied to entities.
- Power inventories read and written from commands, including each slot's item id as plain text.
- Simple Voice Chat integration: conditions for talking, whispering and groups, plus voice broadcast, forced mute and forced deafen.
- Hardcore Revival integration: an
is_downedcondition for players knocked out in Hardcore Revival. - Commands:
/tma with(variables in any command),/tma powerinv(read and writeorigins:inventorypowers),/tma phantom,/tma disguise,/tma global,/tma fakeblock,/tma powertag,/disableshield,/tmavc.
The pages are meant to be read in order, like slides, but each one also stands alone as a reference. Every page has ← previous · contents · next → links at the top and bottom.
What's new in 2.2.0
2.2.0 has breaking changes. Read Upgrading to 2.2.0 before updating a pack that uses shield_block_dot or phantom ids.
New types
| Id | Kind | |
|---|---|---|
recent_damage_blocked | entity condition, attribute | damage blocked with a shield in the last window_ticks ticks (replaces shield_block_dot) |
phantom_action | entity action | runs actions on your phantoms, or one by id |
phantom | entity condition | counts your phantoms, or checks one by id |
action_on_phantom_hit | power type | runs when a viewer hits one of your phantoms |
Phantoms
- Powers can reach phantoms through the three types above and the new
/tma phantom run <id> <command>, which runs a command with the phantom as@s. What works on a phantom lists what actions can and can't do to one. - Ids are whole numbers, handed out automatically (1, 2, 3, …) unless you choose one.
/tma phantom spawnandskinreturn the id and say it in their feedback, soexecute store resultcan keep it for/tma phantom remove, which suggests the ids of live phantoms. - New behavior
stationary: the phantom never moves and only turns to look at nearby viewers. - Fake players can move:
hostile,neutralandpassivenow work for them too, with the normal walk animation. Left out, a fake player is stillstationary./tma phantom skintakes a behavior likespawndoes. - Phantoms can't be hurt or killed by anything, including
/kill, so a phantom mob can't drop real loot. Remove one withdespawn_phantom. - Fixed: fake players wore a default skin (Steve, Alex, …) instead of the named account's. TMA now asks Mojang for signed skin data, which the client requires. A misspelled username, or an account with no skin, is now logged.
Other changes
- Pehkui is no longer required. TMA never used it.
- Hardcore Revival is now listed as a suggested mod, next to Simple Voice Chat. It stays optional.
Not yet tested in-game. Everything above was checked against Minecraft's, Apoli's and Fabric API's code and builds, but none of it has been run in a live game. Treat the (check in-game) notes on each page as exactly that.
Upgrading to 2.2.0
A power that uses a removed id or an old field type fails to load, and the log names the problem.
| Old | New |
|---|---|
shield_block_dot condition and attribute | Removed. Use recent_damage_blocked for how much was blocked. Its angle can still be worked out in a power from where the attacker is. |
"id": "mypack:spirit" on spawn_phantom / despawn_phantom | A whole number, 1 or more: "id": 7001. Pick high numbers so they stay clear of the ids commands hand out. |
tma phantom spawn <id> <owner> … | tma phantom spawn <owner> … [<id>]: the id is optional and goes last. The same for skin. |
tma phantom remove mypack:spirit | tma phantom remove 7001 |
/tma phantom spawn and skin return 1 | They return the phantom's id. |
- Fake players from
spawn_phantomwith abehaviorset now walk. Before, the field was ignored for them. Remove it, or set"behavior": "stationary", to keep them standing still.
What's new in 2.1.3
Everything in 2.1.3 is new. Nothing was removed or renamed, and no field changed meaning, so a pack written for 2.1.0 keeps working as it is.
New conditions
| Id | Kind | |
|---|---|---|
stat | entity | a vanilla statistic (blocks mined, jumps, mobs killed, …) |
is_sleeping | entity | in bed right now |
redstone_power | block, entity | redstone signal 0–15, received, strong or emitted |
has_power_tag | entity, bi-entity | holds a power in a power tag |
last_hit_was_crit | entity | the last attack it made was a critical |
last_damage_was_crit | entity | the last damage it took was a critical |
TMA now has block conditions at all, on the new page 5.
New actions
| Id | Kind | |
|---|---|---|
set_rotation, look_at | entity, bi-entity | turn an entity's view, instantly or a step at a time |
play_block_crack | block | the mining-crack overlay, with no real mining |
spawn_phantom | entity | a mob only chosen players can see |
despawn_phantom | entity | removes a phantom by id, or all of one entity's |
disguise, undisguise | entity | make an entity look like another type, per viewer |
set_fake_block, clear_fake_block | entity, block | show chosen players a different block |
emit_signal | entity | announce something other powers can react to |
modify_global, delete_global | entity, bi-entity | change the global store from JSON |
New power types
| Id | |
|---|---|
action_on_sleep_start | runs when the holder falls asleep |
action_on_chat | runs when a chat message contains your text, sent or received |
action_on_signal | runs when a matching signal is emitted |
fake_blocks | blocks that look different near the holder |
global_variables | declared fields every holder shares |
Bigger additions
- The global store (new page 12): typed values and lists addressed by a key instead of an entity, saved with the world, with optional locking and cleanup for keys tied to entities.
- Power tags (new page 16): group powers in one datapack file, wildcards included, then filter on them.
- Phantoms can be named and removed: an
idonspawn_phantom, unique across the server, plusdespawn_phantomand/tma phantom remove. - Fake players: a phantom shaped like a player, wearing a skin looked up by username, with no tab-list row and no name tag.
- New in code:
Death.*(where you last died),Stat.get(vanilla statistics) andGlobal.*. - New attributes:
last_death_distanceandredstone_power. - New commands:
/tma phantom,/tma disguise,/tma global,/tma fakeblockand/tma powertag. - New gamerule
tmaPlayerExpiryDays(real days, default 30,0= never): how long a player's tracked values and the global keys bound to them are kept after they stop logging in. - Fixed: tracker maps no longer grow forever. A mob's tracked values are dropped when it's killed, removed or unloaded, and a player's when they pass the gamerule above (page 1).
Not yet tested in-game. Everything above was checked against Minecraft's and Apoli's code, and the parser and store logic were tested on their own, but none of it has been run in a live game. Treat the (Suspected) notes on each page as exactly that.
Upgrading to 2.1.0
2.1.0 removes types that Origins 1.10.0 already provides. A power that uses a removed id fails to load, and the log names the unknown type. Swap in the Origins type:
Removed (too-many-attributes:…) | Kind | Use instead |
|---|---|---|
fall_distance | entity condition | origins:fall_distance |
oxygen_remaining | entity condition | origins:air |
is_gliding | entity condition | origins:fall_flying |
potion_amplifier | entity condition | origins:status_effect with min_amplifier / max_amplifier |
sky_visible | entity condition | origins:exposed_to_sky |
wall_collision | entity condition | origins:collided_horizontally |
weather_intensity | entity condition | origins:in_rain |
is_targeting | bi-entity condition | origins:attack_target |
durability_percent | item condition | origins:relative_durability |
- Attributes removed with them:
fall_distance,oxygen_remaining,is_gliding,sky_visible,wall_collision,rain_levelandthunder_level. In functions,data get entity @s FallDistanceanddata get entity @s Airread the same vanilla values. potion_amplifierchecks with a comparison become a range. For example,">=", 1becomes"min_amplifier": 1. For "doesn't have the effect", useorigins:status_effectwith"inverted": true.- New:
friction, a condition and attribute for the effective slipperiness of the block you move on, includingorigins:modify_slipperinesspowers. - New:
is_downed, a condition and attribute for whether a player is knocked out in Hardcore Revival - optional,falsewithout it installed. - New:
drag, a settable attribute on every living entity (not just players) that's a direct stand-in for vanilla's fall-drag constant, without touching gravity or jump height. - Fixed:
has_attack_targetnow works for goal-based mobs (zombies, skeletons, …), not just brain-based ones (piglins, wardens, …).
Pages
| # | Page | What's in it |
|---|---|---|
| 1 | Getting started | Requirements, installing, your first TMA power, how these docs are laid out |
| 2 | Entity conditions | The 40 condition types: combat, movement, body, world, mobs, items, integrations |
| 3 | Bi-entity conditions | is_observed_by, has_power_tag |
| 4 | Damage conditions | damage_source_nbt |
| 5 | Block conditions | redstone_power |
| 6 | Entity actions | signals, fake blocks, set_vibration_cooldown, set_item_cooldown, set_rotation, look_at, spawn_phantom, disguise, undisguise, the block action play_block_crack, phantom_action, the phantom condition, action_on_phantom_hit, the event powers (death, join, disconnect, start tracking, mob conversion, sleep start), action_on_chat, and where the other actions live |
| 7 | Attributes | The 45 read-only attributes, plus the settable drag |
| 8 | Variables | Declaring, changing and checking per-player variables; HUD bars |
| 9 | Variables in Origins fields, commands and scoreboards | with_variables, /tma with, Scoreboard.get, modifier resource |
| 10 | Language reference | The Java expression language: operators, types, built-ins (including Death, Stat and Global), errors |
| 11 | Lists and loops | list, power_list, for_each, for_each_read |
| 12 | Global store | Values shared by every entity: Global.*, global_variables, modify_global, locking, cleanup, /tma global |
| 13 | Voice chat | Simple Voice Chat conditions, broadcast, mute and deafen |
| 14 | Power inventories | Reading and writing origins:inventory powers |
| 15 | Commands | Every command in one place |
| 16 | Power tags | Grouping powers in a datapack file, the has_power_tag condition, /tma powertag |
| 17 | Cookbook | Complete powers that combine several features |
Every id
Condition types
| Id | Kind | Page |
|---|---|---|
attack_cooldown_progress | entity | 2 |
biome_temperature | entity | 2 |
critical_hit_eligible | entity | 2 |
crouch_depth | entity | 2 |
damage_source_nbt | damage | 4 |
distance_to_nearest_player | entity | 2 |
exhaustion | entity | 2 |
expression | entity, bi-entity | 8 |
friction | entity | 2 |
glide_ratio | entity | 2 |
has_attack_target | entity | 2 |
has_power_tag | entity, bi-entity | 16 |
hurt_time | entity | 2 |
in_same_voice_group | bi-entity | 13 |
in_voice_group | entity | 13 |
in_voice_range_of | bi-entity | 13 |
invulnerable_ticks | entity | 2 |
is_blocking | entity | 2 |
is_broadcasting_voice | entity | 13 |
is_deafened_voice | entity | 13 |
is_jumping | entity | 2 |
is_moving_loudly | entity | 2 |
is_muted_voice | entity | 13 |
is_observed_by | bi-entity | 3 |
is_sleeping | entity | 2 |
is_talking | entity | 13 |
is_whispering | entity | 13 |
item_on_cooldown | entity | 2 |
last_attack_reach | entity | 2 |
last_damage_amount | entity | 2 |
last_damage_was_crit | entity | 2 |
last_damage_type | entity | 2 |
last_hit_was_crit | entity | 2 |
light_at_eyes | entity | 2 |
look_angle | entity | 2 |
moon_phase | entity | 2 |
phantom | entity | 6 |
projectile_age | entity | 2 |
recent_damage_taken | entity | 2 |
recent_damage_blocked | entity | 2 |
recent_knockback | entity | 2 |
redstone_power | block, entity | 5, 2 |
regional_difficulty | entity | 2 |
sprint_ticks | entity | 2 |
stat | entity | 2 |
submersion_depth | entity | 2 |
time_airborne | entity | 2 |
time_since_last_meal | entity | 2 |
velocity | entity | 2 |
vibration_on_cooldown | entity | 2 |
voice_installed | entity | 13 |
voice_mic_disabled | entity | 13 |
with_variables | entity, bi-entity | 9 |
xp_progress | entity | 2 |
Action types
| Id | Kind | Page |
|---|---|---|
clear_fake_block | entity, block | 6 |
delete_global | entity, bi-entity | 12 |
despawn_phantom | entity | 6 |
disguise | entity | 6 |
execute | entity, bi-entity | 8 |
for_each | entity, bi-entity | 11 |
for_each_read | entity, bi-entity | 11 |
emit_signal | entity | 6 |
look_at | entity, bi-entity | 6 |
modify_global | entity, bi-entity | 12 |
modify_variable | entity, bi-entity | 8 |
phantom_action | entity | 6 |
play_block_crack | block | 6 |
set_fake_block | entity, block | 6 |
set_item_cooldown | entity | 6 |
set_rotation | entity | 6 |
set_vibration_cooldown | entity | 6 |
spawn_phantom | entity | 6 |
start_broadcast_voice | entity | 13 |
stop_broadcast_voice | entity | 13 |
undisguise | entity | 6 |
with_variables | entity, bi-entity | 9 |
Power types
| Id | Page |
|---|---|
variables | 8 |
variable_bar | 8 |
global_variables | 12 |
list | 11 |
power_list | 11 |
item_list | 11 |
action_on_death | 6 |
action_on_join | 6 |
action_on_disconnect | 6 |
action_on_start_tracking | 6 |
action_on_mob_conversion | 6 |
action_on_sleep_start | 6 |
action_on_signal | 6 |
fake_blocks | 6 |
action_on_chat | 6 |
action_on_phantom_hit | 6 |
Commands
| Command | Page |
|---|---|
/tma with <targets> <command> | 9, 15 |
/tma phantom spawn <owner> <entity_type> <pos> <lifetime> [behavior] [viewers] [id] | 6, 15 |
/tma phantom run <id> <command> | 6, 15 |
/tma disguise <targets> <entity_type> [duration] [viewers], /tma undisguise <targets> | 6, 15 |
/tma global get|set|delete|list|prune|lock|unlock … | 12, 15 |
/tma fakeblock set|clear … | 6, 15 |
/tma powertag list|of … | 16 |
/tma powerinv … | 14, 15 |
/disableshield <targets> <ticks> | 15 |
/tmavc broadcast|mute|deafen … | 13, 15 |
The 45 read-only attributes (plus drag) are listed on page 7.
Tip: ← and → turn pages.
Page 1 of 17
Getting started
These docs cover TMA 2.2.0. What that version changed is listed in What's new, and what a pack needs to change in Upgrading to 2.2.0.
Requirements
| Version | ||
|---|---|---|
| TMA | 2.2.0 | |
| Minecraft | 1.20.1 | |
| Fabric Loader | 0.19.3 or newer | |
| Fabric API | any | |
| Origins | 1.10.0 or newer | includes Apoli and Calio, which TMA builds on |
| Java | 17 or newer | |
| Simple Voice Chat | any, optional | only needed for the voice chat features (page 13) |
| Hardcore Revival | any, optional | only needed for is_downed (page 2) |
Install TMA like any Fabric mod, on the server and on every client. Clients need it because some values are synced to them, for example variables shown in HUD bars and the attributes.
Your first TMA power
A TMA type goes anywhere Origins accepts that kind of type. Use its id with the too-many-attributes: namespace.
data/mypack/powers/steady_aim.json: +20% damage while you stand still and your attack is fully charged.
{
"type": "origins:modify_damage_dealt",
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "too-many-attributes:attack_cooldown_progress", "comparison": ">=", "compare_to": 1.0 },
{ "type": "too-many-attributes:sprint_ticks", "comparison": "==", "compare_to": 0 },
{ "type": "too-many-attributes:time_airborne", "comparison": "==", "compare_to": 0 }
]
},
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.2 }
}Add the power to an origin as usual ("powers": ["mypack:steady_aim"] in data/mypack/origins/<origin>.json), or grant it directly to test:
power grant @s mypack:steady_aimHow these docs are laid out
Each type gets a block like the Origins docs:
Sprint ticks
too-many-attributes:sprint_ticksentity conditionWhat it checks, in one or two sentences.
Field Type Default Description An example with an explanation.
- Field types use the Origins names: Comparison (
"<","<=",">",">=","==","!="), Integer, Float, Double, String, Identifier, and the Origins "type" data types, such as Bi-entity condition type. - Default: required means the field has no default and must be given.
- Enum fields (like
axisorcomponent) accept any capitalization:"y"and"Y"both work. - Confidence notes. Descriptions come from the mod's source code. A note marked (Suspected) is an inference that hasn't been confirmed. Check it in-game before relying on it.
Things that apply to every page
- Ticks. A tick is 1/20 of a second: 20 ticks = 1 second, 1200 ticks = 1 minute.
- Bi-entity pairs. In a bi-entity condition or action, the actor is the first entity, usually the power holder (
self). The target is the second entity, the one being hit, looked at, and so on (other). This matches Origins. Tracked values live on the server and aren't saved. Several conditions read values TMA records as things happen: last damage, recent damage, knockback, attack reach, shield angle, time since last meal, airborne and sprint ticks, crouch depth, vibration cooldown, and the voice chat state. They are:
- kept in memory on the server, per entity
- reset when the server restarts, and never written to the world
- forgotten when a mob or other non-player entity leaves the world: killed, removed, or unloaded with its chunk. Changing dimension keeps them.
- kept for players through deaths and logouts, until the player has been offline longer than the gamerule
tmaPlayerExpiryDays(real days, default30,0= never). The global store uses the same gamerule for keys bound to a player. - 0 / false on the client. A condition Origins checks on the client, such as a
hud_rendercondition, reads them as 0 or false. The attributes are synced to clients every tick, so client-side checks can useorigins:attributeinstead.
- Players versus other entities. Some conditions only work on players (they're false for anything else). Each entry says so.
Tip: ← and → turn pages.
Page 2 of 17
Entity conditions
TMA's 40 general entity conditions. They go anywhere Origins accepts an entity condition: a power's condition, origins:and/or, if_else, a target_condition, and so on.
Most of them compare a number, the same way Origins' own number conditions do:
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | How to compare the value with compare_to: "<", "<=", ">", ">=", "==" or "!=" |
compare_to | Integer, Float or Double (see each entry) | required | The value to compare with |
Every entry below also lists its attribute (page 7), when it has one. Voice chat conditions are on page 13, and expression / with_variables on pages 8 and 9.
is_downed (below) is an optional integration: it needs Hardcore Revival installed, and is simply false without it.
"Tracked" marks a value TMA records while the game runs. It lives on the server, resets on restart, and reads as 0 on the client (page 1).
At a glance
| Group | Conditions |
|---|---|
| Combat | attack_cooldown_progress, critical_hit_eligible, last_attack_reach, hurt_time, invulnerable_ticks, recent_damage_taken, recent_damage_blocked, last_damage_amount, last_damage_type, recent_knockback, last_hit_was_crit, last_damage_was_crit, is_blocking |
| Movement | velocity, look_angle, is_jumping, time_airborne, sprint_ticks, crouch_depth, glide_ratio, friction, is_moving_loudly, submersion_depth |
| Body | exhaustion, time_since_last_meal, xp_progress, stat, is_sleeping |
| World | light_at_eyes, biome_temperature, moon_phase, regional_difficulty, distance_to_nearest_player, redstone_power |
| Powers | has_power_tag |
| Mobs and other entities | has_attack_target, projectile_age |
| Phantoms | phantom, documented with the other phantom types on page 6 |
| Items | item_on_cooldown |
| Vibrations | vibration_on_cooldown |
| Integrations | is_downed |
Removed in 2.1.0 because Origins already has them. Use the Origins type instead:
| Removed | Use |
|---|---|
fall_distance | origins:fall_distance |
oxygen_remaining | origins:air |
is_gliding | origins:fall_flying |
potion_amplifier | origins:status_effect with min_amplifier / max_amplifier |
sky_visible | origins:exposed_to_sky |
wall_collision | origins:collided_horizontally |
weather_intensity | origins:in_rain |
Their attributes were removed too. The full list, including bi-entity and item conditions, is in Upgrading to 2.1.0.
Combat
Attack cooldown progress
too-many-attributes:attack_cooldown_progressentity conditionplayers only
How charged the player's attack is, from 0.0 (just swung) to 1.0 (fully charged). This is the value behind vanilla's attack indicator.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required | 0.0 – 1.0 |
Example:
{
"type": "origins:modify_damage_dealt",
"condition": { "type": "too-many-attributes:attack_cooldown_progress", "comparison": "<", "compare_to": 0.5 },
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.5 }
}Hits made before the attack is half charged deal half damage, on top of vanilla's own reduction.
Attribute: attack_cooldown_progress
Critical hit eligible
too-many-attributes:critical_hit_eligibleentity conditionplayers only
True when the player is in the air in a way that makes a hit a critical: falling (fall distance above 0), not on the ground, not riding anything, and not sprinting.
No fields.
- This is simpler than vanilla's check. Vanilla also requires a charged attack (above 0.9), not climbing, not in water, and no Blindness. Add
attack_cooldown_progressto get closer.
Example:
{
"type": "origins:modify_damage_dealt",
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "too-many-attributes:critical_hit_eligible" },
{ "type": "too-many-attributes:attack_cooldown_progress", "comparison": ">=", "compare_to": 0.9 }
]
},
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.25 }
}Critical hits deal 25% more, on top of vanilla's critical bonus.
Attribute: critical_hit_eligible
Last attack reach
too-many-attributes:last_attack_reachentity conditionplayers onlytracked
The distance, in blocks, between the player and the entity they last attacked, measured as the attack starts.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Double | required | blocks |
- It's
0until the player first attacks, and keeps its value until the next attack. - It's recorded at the start of the attack, before damage is worked out, so inside
modify_damage_dealtit's the distance of the current hit. (From source.)
Example:
{
"type": "origins:modify_damage_dealt",
"condition": { "type": "too-many-attributes:last_attack_reach", "comparison": ">", "compare_to": 2.5 },
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.25 }
}Hits landed from more than 2.5 blocks away deal 25% more: a spear-tip bonus.
Attribute: last_attack_reach
Hurt time
too-many-attributes:hurt_timeentity condition
Ticks left in the entity's "just got hurt" animation (the red flash). Set to 10 when it takes damage, then counts down by 1 each tick to 0.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | ticks, 0 – 10 |
- False for entities that aren't living (like boats).
Example:
{
"type": "origins:modify_damage_dealt",
"condition": { "type": "too-many-attributes:hurt_time", "comparison": ">", "compare_to": 0 },
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.3 }
}For half a second after being hurt, your hits deal 30% more: retaliation.
Attribute: hurt_time
Invulnerable ticks
too-many-attributes:invulnerable_ticksentity condition
Ticks left in the entity's damage cooldown. When hurt, it's set to 20 and counts down. While it's above 10, a new hit only gets through if it's stronger than the last one.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | ticks |
Example:
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "too-many-attributes:invulnerable_ticks", "comparison": ">", "compare_to": 10 },
"entity_action": {
"type": "origins:execute_command",
"command": "particle minecraft:enchanted_hit ~ ~1 ~ 0.3 0.5 0.3 0 2"
}
}Sparkles while you're protected from weaker follow-up hits.
Attribute: invulnerable_ticks
Recent damage taken
too-many-attributes:recent_damage_takenentity conditiontracked
The total damage the entity took in the last window_ticks ticks.
| Field | Type | Default | Description |
|---|---|---|---|
window_ticks | Integer | 60 | How far back to add up, in ticks. At most 600 (30 seconds); larger values act like 600. |
comparison | Comparison | required | |
compare_to | Float | required | damage points (2 = one heart) |
- It adds up the raw damage of each hit as it arrives, before armor, enchantments and effects reduce it. (From source.)
- Hits fully blocked by a shield aren't counted; recent damage blocked adds those up instead.
- (From source) Hits ignored because of the damage cooldown (invulnerable ticks) are still counted.
- False for entities that aren't living.
Example: berserk.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": {
"type": "too-many-attributes:recent_damage_taken",
"window_ticks": 100,
"comparison": ">",
"compare_to": 10
},
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:strength", "duration": 40, "amplifier": 0 }
}
}After taking more than 10 damage (5 hearts) within 5 seconds, you get Strength, refreshed every second while that stays true.
Attribute: recent_damage_taken (always a 60-tick window)
Recent damage blocked
too-many-attributes:recent_damage_blockedentity conditiontracked
The total damage the entity blocked with a shield in the last window_ticks ticks. The blocked-hits counterpart of recent damage taken.
| Field | Type | Default | Description |
|---|---|---|---|
window_ticks | Integer | 60 | How far back to add up, in ticks. At most 600 (30 seconds); larger values act like 600. |
comparison | Comparison | required | |
compare_to | Float | required | damage points (2 = one heart) |
- It adds up the raw damage of each blocked hit, as it would have been before armor. (From source.)
- Hits that got past the shield aren't counted; those go to recent damage taken.
- (From source) Blocked hits during the damage cooldown (invulnerable ticks) are still counted.
- False for entities that aren't living.
Example: shield bash.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": {
"type": "too-many-attributes:recent_damage_blocked",
"window_ticks": 100,
"comparison": ">=",
"compare_to": 12
},
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:strength", "duration": 40, "amplifier": 0 }
}
}After blocking 12 or more damage within 5 seconds, you get Strength, refreshed every second while that stays true.
Attribute: recent_damage_blocked (always a 60-tick window)
Last damage amount
too-many-attributes:last_damage_amountentity conditiontracked
The raw damage of the most recent hit the entity took, before armor and effects.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required | damage points |
- It keeps its value until the next hit, so combine it with
hurt_timefor "just now". - Hits blocked by a shield count here too.
Example: heavy hits stagger you.
{
"type": "origins:action_over_time",
"interval": 1,
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "too-many-attributes:hurt_time", "comparison": ">=", "compare_to": 9 },
{ "type": "too-many-attributes:last_damage_amount", "comparison": ">=", "compare_to": 8 }
]
},
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:slowness", "duration": 20, "amplifier": 1 }
}
}Right after a hit of 8 or more damage, you get Slowness II for a second.
Attribute: last_damage_amount
Last damage type
too-many-attributes:last_damage_typeentity conditiontracked
True when the most recent hit was of this damage type.
| Field | Type | Default | Description |
|---|---|---|---|
value | String | required | The damage type's message id |
- It's the message id (the name death messages use), not the damage type's registry id. For example,
minecraft:player_attackhas the message idplayer. Common ones:fall,player,mob,arrow,lava,inFire,onFire,drown,magic. (From source: vanilla damage type data.) - It's
""until the entity first takes damage, and keeps its value until the next hit.
Example: land roll.
{
"type": "origins:action_over_time",
"interval": 1,
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "too-many-attributes:hurt_time", "comparison": ">=", "compare_to": 9 },
{ "type": "too-many-attributes:last_damage_type", "value": "fall" }
]
},
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:speed", "duration": 40, "amplifier": 1 }
}
}Right after taking fall damage, you get Speed II for 2 seconds.
Recent knockback
too-many-attributes:recent_knockbackentity conditiontracked
The strength of the last knockback the entity received. Vanilla melee knockback is about 0.4, and the Knockback enchantment adds more.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Double | required |
- It's the strength vanilla asked for, before knockback resistance reduces it. (From source.)
- It keeps its value until the next knockback.
Example:
{
"type": "origins:action_over_time",
"interval": 1,
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "too-many-attributes:hurt_time", "comparison": ">=", "compare_to": 9 },
{ "type": "too-many-attributes:recent_knockback", "comparison": ">=", "compare_to": 1.0 }
]
},
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:slow_falling", "duration": 40, "amplifier": 0 }
}
}When you're knocked back hard, you get Slow Falling so you don't take fall damage.
Attribute: recent_knockback
Last hit was crit
too-many-attributes:last_hit_was_critentity conditiontracked
True when the entity's most recent attack was a critical hit: the jump attack, or a fully drawn bow shot. It stays true until that entity attacks again without critting.
No fields.
- Melee and arrows both count. A melee crit is vanilla's own decision (falling, not on the ground, not sprinting, not riding, attack charged), and an arrow counts when it was a critical arrow.
- It's set as the hit resolves, not before it. Inside
origins:modify_damage_dealtororigins:action_on_hitfor that hit, it still holds the previous attack's answer. To react to a crit, run the check a tick later (anaction_over_timewithinterval: 1and arising_action), or usecritical_hit_eligible, which asks whether a hit would crit right now. - It's
falseuntil the entity's first attack, isn't saved, and resets when the server restarts.
Example: after landing a critical hit, you glow briefly.
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "too-many-attributes:last_hit_was_crit" },
"rising_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:glowing", "duration": 40 }
}
}rising_action runs on the tick the condition turns true, which is the tick after the crit lands.
No attribute.
Last damage was crit
too-many-attributes:last_damage_was_critentity conditiontracked
True when the last damage this entity took was a critical hit, melee or a critical arrow. Any other damage, including a normal hit, fall damage or fire, sets it back to false.
No fields.
- It's set just after that damage is applied, so like the condition above it isn't ready during the hit itself. Check it from a later tick.
- It's
falseuntil the entity is first damaged, and isn't saved.
Example: being critted makes you flee-footed for a moment.
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "too-many-attributes:last_damage_was_crit" },
"rising_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:speed", "duration": 60, "amplifier": 1 }
}
}No attribute.
Is blocking
too-many-attributes:is_blockingentity condition
True while the entity is blocking with a shield (after the shield has finished rising).
No fields.
Example:
{
"type": "origins:modify_damage_taken",
"condition": { "type": "too-many-attributes:is_blocking" },
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.2 }
}While blocking, hits that get past the shield (from the side or behind) deal 20% less.
Attribute: is_blocking
Movement
Velocity
too-many-attributes:velocityentity condition
The entity's velocity along one axis, in blocks per tick.
| Field | Type | Default | Description |
|---|---|---|---|
axis | String | required | x, y or z. y is up (negative while falling). |
comparison | Comparison | required | |
compare_to | Double | required | blocks per tick (1 block/tick = 20 blocks/second) |
Example: feather fall.
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "too-many-attributes:velocity", "axis": "y", "comparison": "<", "compare_to": -1.0 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:slow_falling", "duration": 10, "amplifier": 0 }
}
}When you fall faster than 1 block per tick, you get Slow Falling.
Attributes: velocity_x, velocity_y, velocity_z
Look angle
too-many-attributes:look_angleentity condition
Where the entity is looking.
| Field | Type | Default | Description |
|---|---|---|---|
component | String | required | pitch (up/down) or yaw (left/right) |
comparison | Comparison | required | |
compare_to | Float | required | degrees |
- Pitch:
−90is straight up,0level,90straight down. - Yaw:
0faces south (+Z),90west,±180north,−90east. (Suspected: the raw yaw isn't wrapped, so after turning around several times it can be outside −180 – 180. The attribute is clamped to that range.)
Example: stargazer.
{
"type": "origins:action_over_time",
"interval": 40,
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "too-many-attributes:look_angle", "component": "pitch", "comparison": "<=", "compare_to": -70 },
{ "type": "origins:daytime", "inverted": true },
{ "type": "origins:exposed_to_sky" }
]
},
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:regeneration", "duration": 50, "amplifier": 0 }
}
}Looking up at the open night sky regenerates you.
Attributes: look_pitch, look_yaw
Is jumping
too-many-attributes:is_jumpingentity condition
True while the entity's jump input is held.
No fields.
Attribute: is_jumping
Time airborne
too-many-attributes:time_airborneentity conditionplayers onlytracked
Ticks since the player was last on the ground. 0 while on the ground.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | ticks |
- Anything that isn't "on the ground" counts: jumping, falling, swimming, flying and gliding.
Example: glider.
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "too-many-attributes:time_airborne", "comparison": ">", "compare_to": 60 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:slow_falling", "duration": 10, "amplifier": 0 }
}
}After 3 seconds in the air, you fall slowly.
Attribute: time_airborne
Sprint ticks
too-many-attributes:sprint_ticksentity conditionplayers onlytracked
Ticks the player has been sprinting without stopping. Goes back to 0 the moment they stop.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | ticks |
Example: second wind.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": { "type": "too-many-attributes:sprint_ticks", "comparison": ">=", "compare_to": 60 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:speed", "duration": 30, "amplifier": 0 }
}
}After 3 seconds of non-stop sprinting, you get Speed.
Attribute: sprint_ticks
Crouch depth
too-many-attributes:crouch_depthentity conditionplayers onlytracked
How far into a sneak the player is, from 0.0 (standing) to 1.0 (fully crouched). It moves a third of the way toward 1 each tick while sneaking, and back toward 0 when not, so a full crouch takes 3 ticks.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required | 0.0 – 1.0 |
- Only players are tracked; it's
0for other entities.
Example:
{
"type": "origins:modify_damage_taken",
"condition": { "type": "too-many-attributes:crouch_depth", "comparison": ">=", "compare_to": 1.0 },
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.1 }
}While fully crouched, you take 10% less damage.
Attribute: crouch_depth
Glide ratio
too-many-attributes:glide_ratioentity condition
While flying with an elytra: horizontal speed divided by vertical speed. High values mean a flat glide; low values mean a steep dive. With almost no vertical movement, it's the horizontal speed × 1000.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required |
- False when not gliding, whatever the comparison.
Example:
{
"type": "origins:action_over_time",
"interval": 2,
"condition": { "type": "too-many-attributes:glide_ratio", "comparison": "<", "compare_to": 1.0 },
"entity_action": {
"type": "origins:execute_command",
"command": "particle minecraft:cloud ~ ~ ~ 0.2 0.2 0.2 0 3"
}
}A trail of cloud particles while you dive steeply.
Attribute: glide_ratio (0 when not gliding)
Is moving loudly
too-many-attributes:is_moving_loudlyentity condition
True while the entity is on the ground, not crouching, and moving faster than 0.15 blocks per tick horizontally, like walking or running.
No fields.
- For players, this also drives the automatic vibration cooldown.
Example:
{
"type": "origins:action_over_time",
"interval": 10,
"condition": { "type": "too-many-attributes:is_moving_loudly" },
"entity_action": {
"type": "origins:execute_command",
"command": "particle minecraft:sculk_charge_pop ~ ~0.1 ~ 0.3 0 0.3 0 3"
}
}Sculk particles puff up from your steps while you move loudly.
Attribute: is_moving_loudly
Friction
too-many-attributes:frictionentity condition
The friction the entity gets from the block it's moving on. It's that block's slipperiness, after any origins:modify_slipperiness powers the entity has that apply to that block.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required | slipperiness |
- Block values:
0.6for most blocks,0.8for slime blocks,0.98for ice, packed ice and frosted ice, and0.989for blue ice. Higher is slipperier. - The block is the one vanilla uses for movement: half a block below the feet. That position and the power modifiers are taken exactly as Origins applies them. (From source: Apoli 2.9.2
LivingEntityMixin.) - In the air it still reads whatever is half a block below. That's usually air, which is
0.6. Vanilla only uses block friction on the ground, so addorigins:on_blockwhen that matters. - Reading instead of checking: to test a block's own slipperiness without the power modifiers, use Origins'
origins:on_blockwith anorigins:slipperinessblock condition. This condition, and its attribute, give you the effective value, which Origins can't read.
Example: skates. Faster on anything slippery.
{
"type": "origins:conditioned_attribute",
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "origins:on_block" },
{ "type": "too-many-attributes:friction", "comparison": ">=", "compare_to": 0.9 }
]
},
"modifier": {
"attribute": "minecraft:generic.movement_speed",
"operation": "multiply_base",
"value": 0.3
},
"tick_rate": 10
}On ice, blue ice, or ground a modify_slipperiness power has made slippery, you move 30% faster.
Attribute: friction (clamped to 0 – 2)
Submersion depth
too-many-attributes:submersion_depthentity condition
How many fluid blocks are above the entity's eyes, counting the block at eye level, up to 16. 0 when the entity isn't in water, or when its eyes are above the surface.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required | blocks (always whole) |
Example: water pressure.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": { "type": "too-many-attributes:submersion_depth", "comparison": ">=", "compare_to": 6 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:mining_fatigue", "duration": 30, "amplifier": 0 }
}
}Six or more blocks under water, you get Mining Fatigue.
Attribute: submersion_depth
Body
Exhaustion
too-many-attributes:exhaustionentity conditionplayers only
The player's food exhaustion. Actions like sprinting, jumping and fighting raise it. When it passes 4.0, vanilla takes 4 away and removes a point of saturation or food.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required | usually 0.0 – 4.0 |
Example:
{
"type": "origins:action_over_time",
"interval": 10,
"condition": { "type": "too-many-attributes:exhaustion", "comparison": ">=", "compare_to": 3.5 },
"entity_action": {
"type": "origins:execute_command",
"command": "title @s actionbar {\"text\":\"You feel tired…\",\"color\":\"gray\"}"
}
}A warning just before you lose food.
Attribute: exhaustion
Time since last meal
too-many-attributes:time_since_last_mealentity conditionplayers onlytracked
Ticks since the player's food level last went up.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | ticks |
- TMA notices a meal by the food level rising. (Suspected: anything else that raises it, like Peaceful difficulty or a power that feeds you, counts as a meal too.)
- A player who hasn't eaten since the server started has a very large value, so
">"checks are true.
Example:
{
"type": "origins:action_over_time",
"interval": 100,
"condition": { "type": "too-many-attributes:time_since_last_meal", "comparison": ">", "compare_to": 12000 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:weakness", "duration": 120, "amplifier": 0 }
}
}If you haven't eaten in 10 minutes, you're weakened.
Attribute: time_since_last_meal
XP progress
too-many-attributes:xp_progressentity conditionplayers only
Progress toward the next experience level, from 0.0 to 1.0: how full the XP bar is.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required | 0.0 – 1.0 |
Example:
{
"type": "origins:modify_damage_dealt",
"condition": { "type": "too-many-attributes:xp_progress", "comparison": ">=", "compare_to": 0.9 },
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.1 }
}When you're about to level up, your hits deal 10% more.
Attribute: xp_progress
Stat
too-many-attributes:statentity conditionplayers onlyserver only
Compares one of the player's vanilla statistics: the numbers on the Statistics screen, such as blocks mined, jumps, distance walked, mobs killed or items crafted. They're lifetime totals, saved with the player.
| Field | Type | Default | Description |
|---|---|---|---|
stat | Object | required | Which statistic: { "type": "<stat type>", "id": "<id>" }. Same format as Origins' origins:modify_stat action. |
comparison | Comparison | required | |
compare_to | Integer | required | The count, in the stat's own units (see below). |
Stat types: minecraft:mined, minecraft:crafted, minecraft:used, minecraft:broken, minecraft:picked_up, minecraft:dropped (the id is a block or item), minecraft:killed, minecraft:killed_by (an entity type), and minecraft:custom (the id is a general stat like minecraft:jump, minecraft:deaths, minecraft:play_time or minecraft:walk_one_cm).
- Units are vanilla's. Distances are in centimetres (
walk_one_cm: 100 = 1 block), times are in ticks (play_time), and damage is in tenths of a heart-point (damage_dealt: 10 = 1 damage). - Server only. The client doesn't have stats, so in client-checked places like
hud_renderthis is always false. - To change a stat, use Origins'
origins:modify_stat. For maths on stats (per hour, ratios), useStat.getin code.
Example: "Veteran miner". After mining 10,000 stone, you mine faster.
{
"type": "origins:modify_break_speed",
"condition": {
"type": "too-many-attributes:stat",
"stat": { "type": "minecraft:mined", "id": "minecraft:stone" },
"comparison": ">=",
"compare_to": 10000
},
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.2 }
}No attribute: statistics are an open-ended list.
Is sleeping
too-many-attributes:is_sleepingentity condition
True while the entity is asleep: a player lying in a bed, or a villager sleeping at night.
No fields.
- It turns true the moment sleep starts, not when the night skips. To run something once at that moment, use the
action_on_sleep_startpower. - Sleeping is synced to clients, so this also works in client-checked places like
hud_render. (Suspected; not yet checked in-game.)
Example: a sleeper's regeneration.
{
"type": "origins:action_over_time",
"interval": 40,
"condition": { "type": "too-many-attributes:is_sleeping" },
"entity_action": { "type": "origins:heal", "amount": 1 }
}While you sleep, you heal half a heart every 2 seconds.
No attribute.
World
Light at eyes
too-many-attributes:light_at_eyesentity condition
The light level at the entity's eye height, 0 – 15: the brighter of block light and sky light, with sky light dimmed at night and in rain.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | 0 – 15 |
Example: light-sensitive.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": { "type": "too-many-attributes:light_at_eyes", "comparison": ">=", "compare_to": 12 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:weakness", "duration": 30, "amplifier": 0 }
}
}Bright light weakens you.
Attribute: light_at_eyes
Biome temperature
too-many-attributes:biome_temperatureentity condition
The base temperature of the biome the entity is in, not adjusted for height. For example: snowy plains 0.0, plains 0.8, jungle 0.95, desert and the Nether 2.0, frozen peaks −0.7.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required |
Example: heat-adapted.
{
"type": "origins:action_over_time",
"interval": 40,
"condition": { "type": "too-many-attributes:biome_temperature", "comparison": ">=", "compare_to": 1.5 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:fire_resistance", "duration": 60, "amplifier": 0 }
}
}Fire Resistance in hot biomes (deserts, badlands, savannas and the Nether).
Attribute: biome_temperature
Moon phase
too-many-attributes:moon_phaseentity condition
The current moon phase, 0 – 7. 0 is the full moon and 4 the new moon.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | 0 – 7 |
Example: werewolf.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": {
"type": "origins:and",
"conditions": [
{ "type": "too-many-attributes:moon_phase", "comparison": "==", "compare_to": 0 },
{ "type": "origins:daytime", "inverted": true }
]
},
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:strength", "duration": 30, "amplifier": 1 }
}
}On full-moon nights, you get Strength II.
Attribute: moon_phase
Regional difficulty
too-many-attributes:regional_difficultyentity condition
The local difficulty where the entity stands, 0.0 – 6.75. It grows with the world's difficulty setting, how long players have spent in that chunk, and the moon phase. It's the "Local Difficulty" shown on the F3 screen.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Float | required |
Example:
{
"type": "origins:modify_damage_dealt",
"condition": { "type": "too-many-attributes:regional_difficulty", "comparison": ">=", "compare_to": 4 },
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.2 }
}In dangerous areas, you rise to the challenge: +20% damage.
Attribute: regional_difficulty
Distance to nearest player
too-many-attributes:distance_to_nearest_playerentity condition
The distance, in blocks, to the closest other player in the same dimension. With no other players there, it's a huge number, so ">" checks are true.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Double | required | blocks |
Example: hermit.
{
"type": "origins:action_over_time",
"interval": 40,
"condition": { "type": "too-many-attributes:distance_to_nearest_player", "comparison": ">", "compare_to": 100 },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:regeneration", "duration": 50, "amplifier": 0 }
}
}More than 100 blocks from everyone else, you slowly regenerate.
Attribute: distance_to_nearest_player (clamped to 1,000,000)
Has power tag
too-many-attributes:has_power_tagentity conditionalso a bi-entity condition
True when the entity holds a power in a power tag: a group of powers listed in a datapack file. Full details, the file format and the /tma powertag command are on page 16.
| Field | Type | Default | Description |
|---|---|---|---|
tag | String | required | The tag name, as written in the file. |
include_inactive | Boolean | false | Count powers whose own condition is false too. |
A tag name without a namespace gets the namespace of the power the condition is written in.
{ "type": "too-many-attributes:has_power_tag", "tag": "mypack:magic" }No attribute.
Redstone power
too-many-attributes:redstone_powerentity condition
The redstone signal, 0 to 15, reaching the block the entity's feet are in. It's the entity version of the redstone_power block condition, which has the full description and more modes.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | 0 – 15 |
{ "type": "too-many-attributes:redstone_power", "comparison": ">=", "compare_to": 1 }Attribute: redstone_power
Mobs and other entities
Has attack target
too-many-attributes:has_attack_targetentity condition
True when the entity is a mob that currently has any attack target.
No fields.
- False for anything that isn't a mob, such as players.
- It covers both kinds of mob AI: goal-based mobs like zombies and skeletons (their target), and brain-based mobs like piglins, hoglins and wardens (their attack-target memory). (Fixed in 2.1.0; before that, goal-based mobs were missed.)
- To check whether a mob is targeting a specific entity, use Origins'
origins:attack_targetbi-entity condition.
Example:
{
"type": "origins:modify_damage_dealt",
"target_condition": { "type": "too-many-attributes:has_attack_target" },
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.25 }
}Your hits deal 25% more to mobs that are busy hunting someone.
Projectile age
too-many-attributes:projectile_ageentity condition
How many ticks the entity has existed since it was created or last loaded. It works on any entity, but it's most useful on projectiles: how long an arrow has been flying.
| Field | Type | Default | Description |
|---|---|---|---|
comparison | Comparison | required | |
compare_to | Integer | required | ticks |
- The count isn't saved. It starts again at 0 when the entity is loaded from a chunk (and for a player, when they join).
Example: long-range arrows hurt you less.
{
"type": "origins:modify_damage_taken",
"damage_condition": {
"type": "origins:projectile",
"projectile_condition": { "type": "too-many-attributes:projectile_age", "comparison": ">=", "compare_to": 40 }
},
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.5 }
}Projectiles that were in flight for 2 seconds or more deal half damage to you.
Items
Item on cooldown
too-many-attributes:item_on_cooldownentity conditionplayers only
Checks vanilla's item cooldown: the one that greys out an ender pearl or chorus fruit in the hotbar after you use it, or a shield after an axe hits it. This isn't Origins' origins:cooldown power; that's a separate timer, and nothing in Origins reads this one.
| Field | Type | Default | Description |
|---|---|---|---|
item | Identifier | none | The item to check, like minecraft:ender_pearl. When it's set, slot is ignored. |
slot | String | "weapon.mainhand" | Without item: check whatever item is in this slot. Same names as Item.id: weapon.mainhand, weapon.offhand, armor.head … armor.feet, hotbar.0–8, inventory.0–26, enderchest.0–26. |
comparison | Comparison | none | Optional. With it, compares the ticks left on the cooldown with compare_to. |
compare_to | Integer | 0 | ticks |
- Without
comparisonit's true while the item is on cooldown at all. - With
comparison, an item that isn't on cooldown has0ticks left. - Cooldowns belong to the item type, not the stack: every ender pearl is on the same cooldown. In 1.20.1 there are no cooldown groups, so each item has its own.
- An empty slot is never on cooldown. A bad
slotname is an error when the datapack loads. - Vanilla sends cooldowns to the client, so this also works in client-checked places like
hud_render. - Set one yourself with
set_item_cooldown. Read the ticks in code withItem.cooldown.
Example: a pearl's recoil. While your ender pearl is cooling down, you're slower.
{
"type": "origins:conditioned_attribute",
"condition": { "type": "too-many-attributes:item_on_cooldown", "item": "minecraft:ender_pearl" },
"modifier": {
"attribute": "minecraft:generic.movement_speed",
"operation": "multiply_base",
"value": -0.3
},
"tick_rate": 1
}For the 20 ticks after you throw a pearl, you move 30% slower.
Example: the item in your hand has more than 5 seconds of cooldown left.
{ "type": "too-many-attributes:item_on_cooldown", "comparison": ">", "compare_to": 100 }Attribute: mainhand_cooldown (ticks left on the item in your main hand)
Vibrations
Vibration on cooldown
too-many-attributes:vibration_on_cooldownentity conditiontracked
True while the entity's vibration cooldown is above 0. This is TMA's own timer, not vanilla sculk:
- Set it with
set_vibration_cooldown. - For players, TMA sets it to 40 ticks automatically each time they move loudly while it's 0.
- It counts down by 1 every tick.
No fields.
For a moving player, it turning true means "a vibration just happened". Origins' rising_action runs on exactly that moment.
Example:
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "too-many-attributes:vibration_on_cooldown" },
"rising_action": {
"type": "origins:execute_command",
"command": "playsound minecraft:block.sculk_sensor.clicking player @a ~ ~ ~ 1 1"
}
}Everyone nearby hears a sculk click each time you make a vibration, at most every 2 seconds.
Attributes: vibration_cooldown (ticks left), vibration_on_cooldown
Integrations
Is downed
too-many-attributes:is_downedentity conditionplayers onlyneeds Hardcore Revival
True while the player is knocked out (downed, awaiting rescue or revival) in Hardcore Revival. Without Hardcore Revival installed, this is always false - TMA loads normally either way.
No fields.
Example: crawl speed while downed.
{
"type": "origins:speed",
"condition": { "type": "too-many-attributes:is_downed" },
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.7 }
}While downed, movement speed drops by 70%.
Attribute: is_downed
Tip: ← and → turn pages.
Page 3 of 17
Bi-entity conditions
A bi-entity condition checks a pair of entities: the actor and the target (page 1). Which entity is which depends on the power or action the condition sits in; the Origins docs say so for each field.
| Id | Where |
|---|---|
too-many-attributes:is_observed_by | this page |
too-many-attributes:has_power_tag | page 16: the target holds a power with that tag |
too-many-attributes:expression | page 8: check both entities' variables |
too-many-attributes:with_variables | page 9: any bi-entity condition with ${…} values |
too-many-attributes:in_same_voice_group | page 13 |
too-many-attributes:in_voice_range_of | page 13 |
Removed in 2.1.0: is_targeting. Use Origins' origins:attack_target, which does the same and also covers zombies, skeletons and other goal-based mobs.
Is observed by
too-many-attributes:is_observed_bybi-entity condition
True when the target is looking toward the actor: the actor is within max_distance blocks of the target, and inside the target's field of view.
| Field | Type | Default | Description |
|---|---|---|---|
fov_degrees | Float | 60.0 | Width of the target's view cone, in degrees. The actor counts as seen when it's within half of this angle of where the target is looking. |
max_distance | Float | 32.0 | The farthest the actor can be, in blocks. |
- Only direction and distance are checked. Blocks don't hide you: an actor behind a wall still counts as observed. (From source.) For line of sight, add Origins'
origins:can_seebi-entity condition in anorigins:and. - The angle is measured from the target's position toward the actor's position, compared with the target's look direction (including pitch).
Example: a backstab.
{
"type": "origins:modify_damage_dealt",
"bientity_condition": {
"type": "too-many-attributes:is_observed_by",
"fov_degrees": 120,
"inverted": true
},
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.5 }
}In modify_damage_dealt, the actor is you and the target is the entity you hit. "inverted": true flips the result, so hits on anything that isn't looking toward you (within a 120° cone) deal 50% more damage.
Tip: ← and → turn pages.
Page 4 of 17
Damage conditions
Removed in 2.1.0: the item condition durability_percent. Use Origins' origins:relative_durability, which compares the same fraction (currentDurability / maxDurability). TMA has no item conditions now.
Damage source NBT
too-many-attributes:damage_source_nbtdamage condition
Checks one NBT value on the entity that directly caused the damage: the arrow or other projectile for ranged hits, or the attacker for melee.
| Field | Type | Default | Description |
|---|---|---|---|
key | String | required | A top-level NBT key of that entity |
value | String | required | The text the value must equal |
- False when there's no direct entity, as with fall damage, fire or drowning.
- The value is compared as text. Strings are compared as they are. Other values use their SNBT form, so a number includes its suffix (
1b,5,1.5d) and a list includes its brackets. Vanilla saves true/false values as bytes, so "true" is1b. (From source.) - Only top-level keys work. There are no paths like
Item.id. - Entity ids aren't in this data; use Origins'
origins:projectiledamage condition for the projectile type.
Example: crossbow bolts hurt you less.
{
"type": "origins:modify_damage_taken",
"damage_condition": {
"type": "too-many-attributes:damage_source_nbt",
"key": "ShotFromCrossbow",
"value": "1b"
},
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.3 }
}Arrows shot from a crossbow deal 30% less damage to you.
Tip: ← and → turn pages.
Page 5 of 17
Block conditions
TMA's block conditions. They go anywhere Origins accepts a block condition: origins:on_block, origins:in_block, origins:block_in_radius, origins:block_collision, origins:action_on_block_use, a raycast's block_condition, and so on.
Redstone power
too-many-attributes:redstone_powerblock conditionalso an entity condition
The redstone signal strength at a block, from 0 to 15. Origins has no way to read redstone.
| Field | Type | Default | Description |
|---|---|---|---|
mode | String | "received" | Which signal to read. See below. |
direction | String | none | Only for mode: emitted: the side the signal leaves through, down, up, north, south, west or east. Left out: the strongest side. |
comparison | Comparison | required | |
compare_to | Integer | required | 0 – 15 |
The three modes are vanilla's three redstone reads:
mode | Reads | Use it for |
|---|---|---|
received | the strongest signal reaching the block from any side | "is this block powered?", the way a lamp, piston or door checks |
strong | the strongest strong power going into the block | whether a solid block is powered hard enough to pass power on to wire next to it |
emitted | what the block itself sends out | wire, torches, levers, buttons, pressure plates and powered solid blocks |
- A block with nothing powering it reads
0in every mode. emittedwith adirectionis what the neighbour on that side would receive from this block. A redstone torch, for example, sends 15 to the sides and up but nothing into the block it's attached to. A solid block that's strongly powered also counts that power as emitted, as vanilla does.- It's read live when the condition runs, so it follows redstone the same tick it changes.
Example: "Current sense". While you stand on a powered block, you're faster.
{
"type": "origins:conditioned_attribute",
"condition": {
"type": "origins:on_block",
"block_condition": { "type": "too-many-attributes:redstone_power", "comparison": ">", "compare_to": 0 }
},
"modifier": { "attribute": "minecraft:generic.movement_speed", "operation": "multiply_base", "value": 0.3 },
"tick_rate": 1
}Example: any fully powered block within 5 blocks of you.
{
"type": "origins:block_in_radius",
"radius": 5,
"block_condition": { "type": "too-many-attributes:redstone_power", "comparison": "==", "compare_to": 15 },
"comparison": ">=",
"compare_to": 1
}Example: a lever that's on, checked from the side the signal leaves through.
{ "type": "too-many-attributes:redstone_power", "mode": "emitted", "direction": "up", "comparison": ">", "compare_to": 0 }As an entity condition
too-many-attributes:redstone_powerentity condition
The received signal at the block the entity's feet are in, the same as the block condition with no mode, run on that block. It has only comparison and compare_to (Integer, 0 – 15).
- The feet block is the one the entity stands in, not the one under it, and
receivedmeans power coming into that block from its neighbours. A pressure plate you stand on doesn't power its own block, so it doesn't count. A strongly powered block under you does. For the block underneath, useorigins:on_blockwith the block condition, as in the first example.
{ "type": "too-many-attributes:redstone_power", "comparison": ">=", "compare_to": 1 }Attribute: redstone_power (the same value, for players)
Tip: ← and → turn pages.
Page 6 of 17
Entity actions
TMA's actions, and the page each one is documented on:
| Id | Kind | Where |
|---|---|---|
too-many-attributes:set_vibration_cooldown | entity | this page |
too-many-attributes:set_item_cooldown | entity | this page |
too-many-attributes:set_rotation | entity | this page |
too-many-attributes:look_at | entity, bi-entity | this page |
too-many-attributes:play_block_crack | block | this page |
too-many-attributes:spawn_phantom | entity | this page |
too-many-attributes:despawn_phantom | entity | this page |
too-many-attributes:phantom_action | entity | this page |
too-many-attributes:disguise | entity | this page |
too-many-attributes:undisguise | entity | this page |
too-many-attributes:emit_signal | entity | this page |
too-many-attributes:set_fake_block | entity, block | this page |
too-many-attributes:clear_fake_block | entity, block | this page |
too-many-attributes:execute | entity, bi-entity | page 8: run variable code |
too-many-attributes:modify_variable | entity, bi-entity | page 8: change one variable, as JSON |
too-many-attributes:with_variables | entity, bi-entity | page 9: any action with ${…} values |
too-many-attributes:for_each | entity, bi-entity | page 11: code for each list element |
too-many-attributes:for_each_read | entity, bi-entity | page 11: an action for each list element |
too-many-attributes:start_broadcast_voice | entity | page 13 |
too-many-attributes:stop_broadcast_voice | entity | page 13 |
too-many-attributes:modify_global | entity, bi-entity | page 12: change a global value |
too-many-attributes:delete_global | entity, bi-entity | page 12 |
This page also covers the event powers: power types that run actions on death, join, disconnect, start tracking, mob conversion and sleep start. It also covers signals (emit_signal and the action_on_signal power), fake blocks (set_fake_block and the fake_blocks power), and the power type action_on_chat, which runs actions on chat messages.
Set vibration cooldown
too-many-attributes:set_vibration_cooldownentity action
Sets the entity's vibration cooldown, in ticks. This is TMA's own per-entity timer, checked with vibration_on_cooldown and the vibration_cooldown attribute. It counts down by 1 every tick.
| Field | Type | Default | Description |
|---|---|---|---|
ticks | Integer | required | The new cooldown. Negative values count as 0, which ends the cooldown. |
How the timer behaves:
- It's not vanilla's sculk cooldown. It doesn't change when sculk sensors or wardens react. It's a value for your own powers to read, for a "vibration" or "noise" mechanic.
- Players set it themselves. At the end of every server tick, a player who moves loudly while the cooldown is
0gets a cooldown of 40 ticks, the same re-trigger delay as a vanilla sculk sensor. So while a player keeps running, it's on cooldown about every 40 ticks, andvibration_on_cooldownturning true means "a vibration just happened". - Setting a long cooldown hides a player's noise for that long, because the automatic 40 only applies when it's
0. - It isn't saved and resets when the server restarts.
Example: "Hush". Press your primary key to stay silent for 10 seconds.
{
"type": "origins:active_self",
"key": { "key": "key.origins.primary_active" },
"cooldown": 600,
"hud_render": { "should_render": true },
"entity_action": {
"type": "too-many-attributes:set_vibration_cooldown",
"ticks": 200
}
}For 200 ticks the cooldown stays above 0. Any power that reacts to fresh vibrations (for example, one that alerts nearby players when vibration_on_cooldown turns true) stays quiet until it runs out.
Set item cooldown
too-many-attributes:set_item_cooldownentity actionplayers only
Puts an item on vanilla's item cooldown, or takes it off. It's the cooldown ender pearls and chorus fruit use: the item greys out in the hotbar and can't be used until it runs out. It works on any item: vanilla checks the cooldown before every right-click use, so a block on cooldown can't be placed and a bow can't be drawn. Attacking with it still works. Check it with item_on_cooldown.
| Field | Type | Default | Description |
|---|---|---|---|
item | Identifier | none | The item, like minecraft:ender_pearl. When it's set, slot is ignored. |
slot | String | "weapon.mainhand" | Without item: the item in this slot. Same names as Item.id. |
ticks | Integer | required | The new cooldown. 0 or less removes it. |
- It replaces any cooldown the item already has, longer or shorter.
- Cooldowns belong to the item type: cooling down the pearl in your hand cools down every pearl.
- An empty slot does nothing. A bad
slotname is an error when the datapack loads. - Vanilla sends the change to the player, so the hotbar shows it. Like vanilla's, it isn't saved: it's gone after relogging.
Example: "Anchor". Getting hit locks your ender pearls for 5 seconds.
{
"type": "origins:action_when_damage_taken",
"entity_action": {
"type": "too-many-attributes:set_item_cooldown",
"item": "minecraft:ender_pearl",
"ticks": 100
},
"cooldown": 1
}Example: clear the cooldown on whatever you're holding.
{ "type": "too-many-attributes:set_item_cooldown", "ticks": 0 }Set rotation
too-many-attributes:set_rotationentity action
Turns the entity to face a direction. Origins has no action that changes where an entity looks.
| Field | Type | Default | Description |
|---|---|---|---|
yaw | Float | none | Left/right angle in degrees, the same as the look_yaw attribute: 0 is south, 90 west, 180 north, -90 east. Left out: yaw isn't changed. |
pitch | Float | none | Up/down angle in degrees: -90 straight up, 90 straight down. Left out: pitch isn't changed. |
relative | Boolean | false | When true, yaw and pitch are added to the current angles instead of replacing them. |
max_step | Float | none | The most each angle turns in one call, in degrees. Run it from origins:action_over_time to turn smoothly. Left out: it turns all the way at once. |
min_step | Float | 0 | A dead zone. When both angles are already within this many degrees of the target, the call does nothing. |
How it works:
- Players: the server sends a teleport that keeps the position and only changes the view, because a player's client decides where they look. Movement isn't reset. (Suspected: no visible rubber-banding. Check in-game.)
- Mobs: the head and body turn too. Their AI may turn them back on its next tick, so run the action every tick if a mob needs to keep facing a way.
- Other entities (armor stands, projectiles) just get the new angles.
- Cost: for players, each call that changes the view sends a packet. Smooth turning with
max_stepsends one per call, so it's more packets than one snap. Setmin_step(for example1) so calls stop once the view is already close enough, which also avoids tiny camera jitters.
Example: "Compass lock". While sneaking, your view turns north, 10 degrees per tick.
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "origins:sneaking" },
"entity_action": {
"type": "too-many-attributes:set_rotation",
"yaw": 180,
"pitch": 0,
"max_step": 10,
"min_step": 1
}
}Look at
too-many-attributes:look_atentity action and bi-entity action
Turns an entity's eyes toward a point, with the same turning rules as set_rotation.
As an entity action, it looks at a position:
| Field | Type | Default | Description |
|---|---|---|---|
x, y, z | Double | required | The point to look at. |
relative_to | String | "world" | world: the numbers are world coordinates. self: they're an offset from the entity's feet, like ~ ~ ~. |
max_step | Float | none | As in set_rotation. |
min_step | Float | 0 | As in set_rotation. |
As a bi-entity action, the actor looks at the target:
| Field | Type | Default | Description |
|---|---|---|---|
anchor | String | "eyes" | Which part of the target to look at: eyes, feet or center (the middle of its hitbox). |
max_step | Float | none | As in set_rotation. |
min_step | Float | 0 | As in set_rotation. |
Example: "Stare down". Hitting a mob turns your view to its eyes.
{
"type": "origins:action_on_hit",
"bientity_action": { "type": "too-many-attributes:look_at", "anchor": "eyes" },
"cooldown": 1
}Example (an action): make everything within 8 blocks of you turn to look at you (for mobs, only until their AI turns them away).
{
"type": "origins:area_of_effect",
"radius": 8,
"bientity_action": {
"type": "origins:invert",
"action": { "type": "too-many-attributes:look_at" }
}
}Play block crack
too-many-attributes:play_block_crackblock action
Shows the crack overlay you see while mining a block, stepping from stage 0 to 9 over a set time, without anyone mining it. Nothing happens to the block itself unless on_finish is break.
It's a block action, so it goes wherever Origins takes one: origins:block_action_at (the block at an entity's position), a raycast's block_action, origins:action_on_block_use, and so on.
| Field | Type | Default | Description |
|---|---|---|---|
duration | Integer | 20 | Ticks from stage 0 to the end of stage 9. |
on_finish | String | "clear" | What happens when the time is up. clear: the overlay goes away. hold: it stays at full crack until the block changes or you run this action with clear. break: the block breaks for real, with the breaking sound and particles. |
drop_items | Boolean | true | With on_finish: break: whether the block drops its items. |
clear | Boolean | false | When true, this call doesn't start a crack. It stops any crack running or held at the block and removes the overlay. |
- One crack per block. While a crack is running or held at a block, starting another one there does nothing.
- It stops early if the block changes (broken, replaced, or its state changes) or its chunk unloads. The overlay is removed.
- Who sees it: the same players who'd see a real player mining it, in that dimension within 32 blocks. Each stage is sent when it starts, so a player who walks into range sees the crack from the next stage on.
- Air can't crack; the action does nothing on an empty block.
- It isn't saved: cracks stop when the server restarts.
Example: "Rot touch". Right-clicking a block with an empty hand makes it decay and break over 3 seconds.
{
"type": "origins:action_on_block_use",
"item_condition": { "type": "origins:empty" },
"block_action": {
"type": "too-many-attributes:play_block_crack",
"duration": 60,
"on_finish": "break"
}
}Spawn phantom
too-many-attributes:spawn_phantomentity action
Spawns a phantom: a mob that only exists on the screens of the players allowed to see it. The server never adds it to the world, so it can't be hit for real, doesn't collide with anyone, and nothing it does changes the world. Everyone else sees nothing. Use it for hallucinations, spirits only one player can see, or jump scares.
| Field | Type | Default | Description |
|---|---|---|---|
entity_type | Identifier | required | What it looks like, like minecraft:zombie. Any type except minecraft:player. It uses the type's default look. |
x, y, z | Double | 0 | Where it spawns. |
relative_to | String | "self" | self: x/y/z are an offset from the entity running the action, like ~ ~ ~. world: they're world coordinates. |
behavior | String | "neutral", "stationary" for fake players | How it moves. See below. |
lifetime | Integer | required | Ticks until it disappears. |
viewer_condition | Bi-entity condition | none | Who can see it. The actor is the viewing player and the target is the entity that spawned it. Left out: every player nearby. |
recheck | Boolean | true | true: viewer_condition is checked again every half second, so players can start or stop seeing it. false: each player's result is kept from the first check. |
id | Integer | assigned | A number, 1 or more, to remove this phantom by later with despawn_phantom. Left out: the next free id is assigned, which a pack can't read back from an action. Ids are unique across the server: spawning with an id that's already alive replaces that phantom. |
skin | String | none | Only with entity_type: "minecraft:player": the username whose skin the fake player wears. |
Behavior. Mobs walk with their own vanilla pathfinding and physics, but none of their normal AI runs. Fake players move the same way. Instead:
behavior | What it does |
|---|---|
hostile | Chases the nearest player who can see it, within the mob's follow range. |
neutral | Wanders around and glances at nearby viewers. |
passive | Wanders like neutral until a viewer hits it, then chases that player. |
stationary | Never moves. It only turns its head to look at nearby viewers. |
- A chasing phantom stops when it reaches its target (within melee reach) and stands facing them. It follows again if they move away.
- Hitting a phantom does no damage, knockback or sound. It makes a
passivephantom chase you and runs its spawner'saction_on_phantom_hitpowers. - Entity types that aren't mobs, such as armor stands, just stay where they spawned. Walking mobs move best. Flying or hopping mobs (bats, slimes) may not move well. (Suspected; check in-game.)
- A phantom is silent. It makes no sound for anyone, including its viewers.
- Whatever its type would normally do can't happen: attacks, arrows, explosions, dropping items, trampling crops, pressing pressure plates, pushing entities or setting off sculk sensors.
- It disappears when its
lifetimeruns out and when the server stops. It isn't saved. It only moves while its chunk is loaded. - With a
viewer_condition, a phantom whose spawner has left (logged out or unloaded) can't be seen by any new viewers.
Example: "Stalker". Every 30 seconds, a zombie only you can see appears 12 blocks behind you and chases you for up to 10 seconds.
{
"type": "origins:action_over_time",
"interval": 600,
"entity_action": {
"type": "too-many-attributes:spawn_phantom",
"entity_type": "minecraft:zombie",
"x": 0, "y": 0, "z": -12,
"behavior": "hostile",
"lifetime": 200,
"viewer_condition": { "type": "origins:equal" }
}
}origins:equal is true when the actor and target are the same entity, so only the spawner sees it. The offset is in world directions, not relative to where you face.
Fake players
Set entity_type to minecraft:player and a phantom looks like a player instead of a mob.
{
"type": "too-many-attributes:spawn_phantom",
"entity_type": "minecraft:player",
"skin": "Notch",
"id": 50,
"x": 0, "y": 0, "z": 2,
"lifetime": 200
}- The skin is looked up by username, the same way the server looks up any account: its own cache first, then Mojang. The lookup happens in the background, so the very first time a name is used the phantom may appear in the default skin for a moment and then change. An offline server, or one with no internet, keeps the default skin.
- It's not in the tab list, and it has no name tag above its head.
- It stands still unless you give it a
behavior. Left out, a fake player isstationary; any other behavior makes it walk, chase and wander like a mob. - How it walks: players have no pathfinding, so an unseen husk (never shown to anyone, and never in the world) does the walking, and the fake player copies where it goes. It moves at a husk's speed, climbs what a husk climbs, and can't open doors. Viewers see the normal player walk animation. (From source; check in-game.)
- Its username and UUID are made up, so it never clashes with the real account, even if that player is online.
From a command: /tma phantom spawn, or /tma phantom skin for a fake player.
Reaching phantoms
A phantom isn't in the world, so nothing that searches the world finds it: selectors like @e, area_of_effect, raycasts and mob targeting all miss it. These TMA types hand the phantom to Origins directly instead.
| Type | Kind | What it does |
|---|---|---|
phantom_action | entity action | runs actions on your phantoms, or one by id |
phantom | entity condition | counts your phantoms, or checks one by id |
action_on_phantom_hit | power | runs when a viewer hits one of your phantoms |
/tma phantom run | command | runs a command as a phantom (@s) |
What works on a phantom (from source; check in-game):
- Looks reach its viewers on the next tick: glowing, invisibility, being on fire, a custom name, pose, and other entity data.
- Movement: velocity (
origins:add_velocity), facing, and teleporting in a command (tp @s). A walking phantom may walk off again right away. - Effects apply and show. They wear off on mobs, but never on fake players, because a fake player doesn't tick. Remove them with
origins:clear_effect. - It can't be hurt or killed, not even by
/kill: dying would drop real loot. Usedespawn_phantominstead. - It can't hold powers. Powers only run on entities that tick in the world, so a phantom is only ever the target.
- The rest of the world is real. An action that spawns particles, plays a sound or summons an entity at the phantom does it for everyone, not only its viewers.
- An action that fails on a phantom (something a player-less entity can't do) is skipped and logged, rather than breaking the power.
Phantom action
too-many-attributes:phantom_actionentity action
Runs actions on phantoms.
| Field | Type | Default | Description |
|---|---|---|---|
id | Integer | none | The phantom to act on, whoever owns it. Left out: every phantom the running entity spawned. |
phantom_condition | Entity condition | none | Only phantoms that pass this. |
entity_action | Entity action | none | Runs on the phantom. |
bientity_action | Bi-entity action | none | The actor is the entity running the action and the target is the phantom. |
Example: your phantoms glow for 5 seconds.
{
"type": "too-many-attributes:phantom_action",
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:glowing", "duration": 100 }
}
}Phantom condition
too-many-attributes:phantomentity condition
Counts the phantoms that match, and compares the count.
| Field | Type | Default | Description |
|---|---|---|---|
id | Integer | none | Only the phantom with this id, whoever owns it. Left out: every phantom this entity spawned. |
entity_condition | Entity condition | none | Checked on the phantom. |
bientity_condition | Bi-entity condition | none | The actor is this entity and the target is the phantom. |
comparison | Comparison | ">=" | |
compare_to | Integer | 1 |
With only the defaults, it's "this entity has at least one phantom".
Example: true while one of your phantoms is within 3 blocks of you.
{
"type": "too-many-attributes:phantom",
"bientity_condition": { "type": "origins:distance", "comparison": "<=", "compare_to": 3 }
}Action on phantom hit
too-many-attributes:action_on_phantom_hitpower type
Runs when a player who can see one of the holder's phantoms hits it. The holder is the phantom's spawner.
| Field | Type | Default | Description |
|---|---|---|---|
id | Integer | none | Only hits on this phantom. Left out: any of the holder's phantoms. |
entity_action | Entity action | none | Runs on the holder. |
bientity_action | Bi-entity action | none | The actor is the player who hit it and the target is the phantom. |
bientity_condition | Bi-entity condition | none | Same actor and target. Only runs when this is true. |
condition | Entity condition | none | Checked on the holder, like any power. |
- It runs for every behavior, and a
passivephantom still starts chasing whoever hit it. - The phantom takes no damage or knockback, so use the actions to show the hit.
Example: a decoy that vanishes when it's hit and gives the attacker Slowness.
{
"type": "too-many-attributes:action_on_phantom_hit",
"bientity_action": {
"type": "origins:and",
"actions": [
{ "type": "origins:actor_action", "action": { "type": "origins:apply_effect", "effect": { "effect": "minecraft:slowness", "duration": 60 } } },
{ "type": "origins:target_action", "action": { "type": "origins:execute_command", "command": "particle minecraft:poof ~ ~1 ~ 0.3 0.5 0.3 0 20" } }
]
},
"entity_action": { "type": "too-many-attributes:despawn_phantom" }
}despawn_phantom without an id removes all of the holder's phantoms. With several out at once, give each an id and a matching action_on_phantom_hit.
Despawn phantom
too-many-attributes:despawn_phantomentity action
Removes phantoms early, before their lifetime runs out.
| Field | Type | Default | Description |
|---|---|---|---|
id | Integer | none | The phantom to remove. Left out: every phantom this entity spawned. |
- Ids are unique across the server, so an
idremoves that phantom whoever spawned it. Without an id, only the running entity's own phantoms go. - Phantoms from
/tma phantomget assigned ids counting up from 1. Picking high numbers for your pack's own ids keeps them clear of those. - Removing a phantom that isn't there does nothing.
- Viewers see it disappear on the next tick.
Example: a spirit you can summon and dismiss with the same key.
{
"type": "origins:active_self",
"key": { "key": "key.origins.secondary_active" },
"cooldown": 20,
"entity_action": { "type": "too-many-attributes:despawn_phantom", "id": 7001 }
}From a command: /tma phantom remove.
Disguise
too-many-attributes:disguiseentity action
Makes other players see the entity as another entity type: a player who looks like a zombie, a cow that looks like a creeper. It's visual only. The entity keeps its own hitbox, health, AI and abilities on the server, and mobs still treat it as what it really is.
| Field | Type | Default | Description |
|---|---|---|---|
entity_type | Identifier | required | What it looks like, like minecraft:zombie. Any type except minecraft:player. It uses the type's default look. |
duration | Integer | 0 | Ticks until the disguise ends. 0 or less: until undisguise or the entity is gone. |
viewer_condition | Bi-entity condition | none | Who sees the disguise. The actor is the viewing player and the target is the disguised entity. Players who fail it see the real entity. Left out: everyone. |
recheck | Boolean | true | true: viewer_condition is checked again every half second, and players whose result changes see the entity swap. false: each player's first result is kept. |
- What shows through: the real entity's held items and armor, when the disguise type can show equipment (zombies, skeletons, armor stands, …). Nothing else does: no name tag, no sneaking or swimming pose, no fire, glowing or invisibility effects. An invisible player in a disguise is still visible as the disguise.
- The disguised player sees their own normal body in third person. Only other players see the disguise.
- The client uses the disguise's size to aim at it, so a small disguise on a big entity can be hard to hit, and the other way round.
- Disguising an entity that's already disguised replaces the disguise. It isn't saved: it ends when the entity unloads, a player logs out, or the server stops.
Example: "Skin of the dead". Your primary key disguises you as a zombie for 30 seconds, for players who don't have your origin.
{
"type": "origins:active_self",
"key": { "key": "key.origins.primary_active" },
"cooldown": 1200,
"entity_action": {
"type": "too-many-attributes:disguise",
"entity_type": "minecraft:zombie",
"duration": 600,
"viewer_condition": {
"type": "origins:actor_condition",
"condition": {
"type": "origins:origin",
"origin": "mypack:ghoul",
"inverted": true
}
}
}
}Undisguise
too-many-attributes:undisguiseentity action
Ends the entity's disguise. Everyone sees the real entity again. No fields. It does nothing if the entity isn't disguised.
{ "type": "too-many-attributes:undisguise" }From a command: /tma disguise and /tma undisguise.
Event powers
Six power types that run actions when something happens to the holder. Origins has no power for any of these moments. They all run on the server only.
| Id | Runs when | Holder | Other entity (bi-entity target) |
|---|---|---|---|
too-many-attributes:action_on_death | the holder dies | any living entity | the killer, if there is one |
too-many-attributes:action_on_join | the holder joins the server | a player | none |
too-many-attributes:action_on_disconnect | the holder leaves the server | a player | none |
too-many-attributes:action_on_start_tracking | the server starts sending an entity to the holder | a player | that entity |
too-many-attributes:action_on_mob_conversion | the holder turns into another mob | a mob | the new mob |
too-many-attributes:action_on_sleep_start | the holder falls asleep | a player or villager | none |
All six accept Origins' usual condition field: the power does nothing while its condition is false. In bi-entity actions and conditions, the actor is always the holder.
Fields:
| Field | Type | Default | On | Description |
|---|---|---|---|---|
entity_action | Entity action | optional | death, join, disconnect, sleep_start | Runs on the holder. |
bientity_action | Bi-entity action | optional | death, start_tracking, mob_conversion | Runs on the holder (actor) and the other entity (target). It's skipped when there's no other entity, e.g. a death with no killer. |
bientity_condition | Bi-entity condition | optional | death, start_tracking, mob_conversion | If set, both actions only run when it's true. Nothing runs if there's no other entity. |
damage_condition | Damage condition | optional | death | If set, both actions only run when the killing damage matches. The amount is the damage before armor. |
What each event means exactly:
action_on_deathruns after the holder has died, so the actions see a dead entity. Use it for effects on the world, the killer or scores, not to save the holder (for that, useorigins:prevent_death). The "killer" is the entity credited by the damage source: the shooter of an arrow, not the arrow.action_on_joinruns once the player is fully in the world, with their powers loaded. That's every login, not just the first one.action_on_disconnectruns while the player is leaving, before their data is saved, so variable changes made here are kept.action_on_start_trackingis about network range, not sight. It fires when the server starts sending an entity to the player. That range depends on the entity type's tracking range, scaled by the server'sentity-broadcast-range-percentageand capped by view distance. Walls, where the player is looking and invisibility don't matter, and it isn't the same as simulation distance. It fires again each time the entity comes back into range, and for every entity near a player who joins or teleports, so keep its condition cheap.action_on_mob_conversioncovers vanilla conversions such as a zombie villager being cured, a zombie drowning into a drowned, a pig or villager struck by lightning, and a tadpole growing up. The powers belong to the old mob. The new mob doesn't inherit them, so the bi-entity action is the place to set it up.action_on_sleep_startruns the moment the holder lies down and falls asleep, before the night is skipped. It only runs for sleep that actually starts: a bed that's too far, a monster nearby, daytime, or anorigins:prevent_sleeppower stop it from firing. (Suspected from how Fabric's event is called; not yet checked in-game.) Origins'origins:action_on_wake_upcovers the other end. To check "asleep right now", useis_sleeping.
Example: "Last words". When you die to another player, they get Glowing for 30 seconds.
{
"type": "too-many-attributes:action_on_death",
"bientity_condition": {
"type": "origins:target_condition",
"condition": { "type": "origins:entity_type", "entity_type": "minecraft:player" }
},
"bientity_action": {
"type": "origins:target_action",
"action": { "type": "origins:apply_effect", "effect": { "effect": "minecraft:glowing", "duration": 600 } }
}
}Signals
too-many-attributes:emit_signalentity action too-many-attributes:action_on_signalpower type
One power announces something by name, and any power listening for that name reacts. Everything else in Origins is either polled or scanned at one instant; signals let two separately written origins work together without either knowing the other's power ids.
Sending:
| Field | Type | Default | Description |
|---|---|---|---|
signal | String | required | The name, like mypack:war_cry. Without a namespace it gets too-many-attributes:. Names are lowercased. |
radius | Double | none | Only entities within this many blocks, in the same dimension, hear it. Left out: every loaded entity in every dimension. |
Listening:
| Field | Type | Default | Description |
|---|---|---|---|
signals | Array of Strings | required | The names to listen for. * matches any run of characters: mypack:*, *:alarm, or * for everything. |
entity_action | Entity action | optional | Runs on the listener. |
bientity_action | Bi-entity action | optional | Actor: the listener. Target: the entity that sent the signal. |
bientity_condition | Bi-entity condition | optional | Both actions only run when it's true, with the same actor and target. |
It also takes Origins' usual condition: a listener ignores signals while it's false.
- The sender never hears its own signals, even with
*. - Any living entity can listen, not just players.
- Signals are instant, not stored: a listener that isn't loaded at that moment misses it.
- A listener's actions can send signals of their own, up to 8 levels deep. Past that they're dropped and the log says so, to stop two powers setting each other off forever.
- Cost: with a
radius, only entities in that area are checked. Without one, every loaded entity in the world is checked, so keep those for rare moments.
Example: "War cry". Your primary key sends a signal 24 blocks around you.
{
"type": "origins:active_self",
"key": { "key": "key.origins.primary_active" },
"cooldown": 200,
"entity_action": { "type": "too-many-attributes:emit_signal", "signal": "mypack:war_cry", "radius": 24 }
}Example: "Rally". Any origin with this power gets Strength when it hears a war cry from a friend.
{
"type": "too-many-attributes:action_on_signal",
"signals": ["mypack:war_cry"],
"bientity_condition": { "type": "origins:in_same_team" },
"entity_action": {
"type": "origins:apply_effect",
"effect": { "effect": "minecraft:strength", "duration": 200 }
}
}Fake blocks
Shows chosen players a different block at a position. The world doesn't change: the real block stays what it was, and only the chosen players are sent something else. Origins' block powers all change the real block.
There are three ways to make one: the set_fake_block action, the fake_blocks power, and /tma fakeblock.
What to know before using any of them:
- Players move by what they see. A fake solid block really stops that player. A fake air block where a real wall stands lets their client walk in, and the server pulls them back. Use fakes for things to look at, or for solid illusions, rather than for fake openings.
- Breaking a fake block doesn't work. The player mines it, the server says the real block is still there, and it comes straight back.
- A real change removes the fake. If the real block is broken, placed or changed by anything, viewers see reality again.
- Fakes aren't saved: they end when the server restarts. They belong to positions, not players, so a player who relogs sees them again.
- Interacting with the real block still happens normally: a fake block over a real chest still opens the chest.
Set fake block
too-many-attributes:set_fake_blockentity action and block action too-many-attributes:clear_fake_blockentity action and block action
| Field | Type | Default | Description |
|---|---|---|---|
block | Block state | required | What viewers see, like minecraft:stone or minecraft:oak_door[facing=east,half=upper]. |
x, y, z | Double | 0 | Entity action only: the position. |
relative_to | String | "self" | Entity action only: self (an offset from the entity) or world. |
viewer_condition | Condition | none | Who sees it. In the entity action it's a bi-entity condition: actor the viewing player, target the entity that set it. In the block action it's an entity condition on the viewing player. Left out: everyone. |
recheck | Boolean | true | Re-check viewer_condition twice a second, so players start and stop seeing it. false keeps each player's first result. |
duration | Integer | 0 | Ticks until it goes away. 0 lasts until it's cleared or the real block changes. |
- The position is fixed when the action runs. It doesn't follow the entity afterwards.
- Setting a fake at a position that already has one replaces it.
clear_fake_blocktakes the same position fields (entity action) or the block it's run on, and removes the fake there.- Nothing happens in an unloaded chunk.
Example: "Wall of illusion". Right-clicking a block with an empty hand makes it look like bedrock, for everyone, for 10 seconds.
{
"type": "origins:action_on_block_use",
"item_condition": { "type": "origins:empty" },
"block_action": {
"type": "too-many-attributes:set_fake_block",
"block": "minecraft:bedrock",
"duration": 200
}
}Fake blocks power
too-many-attributes:fake_blockspower type
While the holder has this power, blocks near them look different. Two modes, and one power can use both.
| Field | Type | Default | Description |
|---|---|---|---|
block | Block state | required | What the matching blocks look like. |
block_condition | Block condition | none | Replace mode: every real block within radius of the holder matching this condition is shown as block. |
radius | Integer | 8 | Replace mode only. Capped at 16. |
offsets | Array of {x, y, z} | none | Offset mode: positions relative to the holder, which move with them. |
rotate_with_facing | Boolean | false | Offset mode: turn the offsets with the holder's facing, like ^ ^ ^ in commands (x left, y up, z forward). |
show_to_holder | Boolean | true | Whether the holder sees them. |
viewer_condition | Bi-entity condition | none | Other players who also see them: actor the viewing player, target the holder. Left out: nobody else. |
interval | Integer | 10 | Ticks between recomputes. |
- Replace mode shows the holder only, unless
viewer_conditionadds others. - Cost: replace mode checks every block in the cube each recompute: radius 8 is about 4,900 blocks, radius 16 about 35,900. Raise
intervalif you use a big radius. - The power's
conditionturns it off: the blocks go back to normal within a couple of seconds.
Example: "Ore sight". While the power is active, stone within 8 blocks looks like glass.
{
"type": "too-many-attributes:fake_blocks",
"block": "minecraft:glass",
"block_condition": { "type": "origins:block", "block": "minecraft:stone" },
"radius": 8,
"interval": 20
}Example: a ghost wall two blocks in front of you, that only other players see.
{
"type": "too-many-attributes:fake_blocks",
"block": "minecraft:cobweb",
"offsets": [{ "x": 0, "y": 0, "z": 2 }, { "x": 0, "y": 1, "z": 2 }],
"rotate_with_facing": true,
"show_to_holder": false,
"viewer_condition": { "type": "origins:distance", "comparison": "<=", "compare_to": 16 }
}Action on chat
too-many-attributes:action_on_chatpower type
Runs actions when a chat message contains certain text. It can react to messages the holder sends (spoken spells, trigger words), messages the holder receives from other players, or both. Origins has no way to read chat.
| Field | Type | Default | Description |
|---|---|---|---|
substrings | Array of Strings | required | The text to look for. Each one can be anywhere in the message. |
match | String | "any" | any: one substring is enough. all: every substring must be in the message. |
ordered | Boolean | false | With match: all: the substrings must appear in the order listed, without overlapping. |
case_sensitive | Boolean | true | When false, capital letters don't matter. |
direction | String | "both" | outgoing: messages the holder sends. incoming: messages other players send while the holder is online. both: either. |
hide_message | Boolean | false | For a matching outgoing message: nobody sees it in chat. It has no effect on incoming messages. |
entity_action | Entity action | optional | Runs on the holder, once per matching message. |
bientity_action | Bi-entity action | optional | Actor: the holder. Target: for an outgoing message, each other online player (once each); for an incoming one, the sender. |
bientity_condition | Bi-entity condition | optional | Filters bientity_action per target, with the same actor and target. It doesn't affect entity_action. |
It also accepts Origins' usual condition: the power ignores chat while it's false.
- Only real chat messages count.
/say,/me,/msg,/telland command blocks don't trigger it. - The text is what the player typed, before any server formatting.
- Several powers can match the same message, and they all run. If any matching outgoing power hides the message, it's hidden.
- A hidden message runs no incoming powers, since nobody received it.
- Your own message never counts as incoming for you, so
direction: bothcan't run a power twice for one message. - Everyone online receives every chat message, so "incoming" means any other player's message, from anywhere in the world.
Example: "Word of fire". Saying "ignis" (in any capitalisation) sets every player within 10 blocks on fire. The word isn't shown in chat.
{
"type": "too-many-attributes:action_on_chat",
"substrings": ["ignis"],
"case_sensitive": false,
"direction": "outgoing",
"hide_message": true,
"bientity_condition": { "type": "origins:distance", "comparison": "<=", "compare_to": 10 },
"bientity_action": {
"type": "origins:target_action",
"action": { "type": "origins:set_on_fire", "duration": 5 }
}
}Example: "Eavesdropper". When anyone says both "where" and "base", in that order, you get a glowing hint on them.
{
"type": "too-many-attributes:action_on_chat",
"substrings": ["where", "base"],
"match": "all",
"ordered": true,
"case_sensitive": false,
"direction": "incoming",
"bientity_action": {
"type": "origins:target_action",
"action": { "type": "origins:apply_effect", "effect": { "effect": "minecraft:glowing", "duration": 200 } }
}
}Tip: ← and → turn pages.
Page 7 of 17
Attributes
TMA registers 45 read-only attributes on every player, mirroring its conditions and refreshed at the end of every server tick, plus one settable attribute - drag - on every living entity. That means you can read most of them without any power:
attribute @s too-many-attributes:time_airborne get
execute store result score @s sprint run attribute @s too-many-attributes:sprint_ticks get
execute store result score @s speed10 run attribute @s too-many-attributes:velocity_y get 10The last number (10) is /attribute's scale. Scores are whole numbers, so scale up to keep decimals.
Why use them:
- In commands and functions, through
/attributeandexecute store. In Origins, with
origins:attribute. This condition works on the client too, because attributes are synced to clients. That makes them the way to use TMA values inhud_renderconditions and other client-checked places (see page 1).{ "type": "origins:attribute", "attribute": "too-many-attributes:crouch_depth", "comparison": ">=", "compare_to": 1 }- In variable code, through a score: store the attribute in a score, then read it with
Scoreboard.get.
Rules:
- Players only. Other entities don't have them. (Exception:
drag, below, which every living entity has.) - Yes/no values are
1or0. - Values are clamped to the range below. For example,
time_since_last_mealstops at 1,000,000,000. - Don't change them. The base value is overwritten every tick. Modifiers added with
/attribute … modifier addwould stay, and would makegetreturn a wrong value. (Exception:drag- it's meant to be changed.)
The list
Attribute (too-many-attributes:…) | Value | Range | Condition |
|---|---|---|---|
attack_cooldown_progress | how charged the attack is | 0 – 1 | attack_cooldown_progress |
hurt_time | hurt animation ticks left | 0 – 40 | hurt_time |
invulnerable_ticks | invulnerability ticks left | 0 – 40 | invulnerable_ticks |
velocity_x / velocity_y / velocity_z | velocity, blocks per tick | −16 – 16 | velocity |
look_pitch | up/down look angle | −90 – 90 | look_angle |
look_yaw | left/right look angle | −180 – 180 | look_angle |
is_jumping | 1 while jumping | 0 / 1 | is_jumping |
crouch_depth | sneak transition | 0 – 1 | crouch_depth |
exhaustion | food exhaustion | 0 – 40 | exhaustion |
xp_progress | progress to next level | 0 – 1 | xp_progress |
biome_temperature | biome base temperature | −2 – 2 | biome_temperature |
regional_difficulty | local difficulty | 0 – 6.75 | regional_difficulty |
light_at_eyes | light level at eye height | 0 – 15 | light_at_eyes |
last_attack_reach | distance of your last attack | 0 – 32 | last_attack_reach |
recent_damage_taken | damage taken in the last 60 ticks | 0 – 2048 | recent_damage_taken (whose window you can change) |
recent_damage_blocked | damage blocked with a shield in the last 60 ticks | 0 – 2048 | recent_damage_blocked (whose window you can change) |
vibration_cooldown | vibration cooldown ticks left | 0 – 2400 | vibration_on_cooldown |
vibration_on_cooldown | 1 while on cooldown | 0 / 1 | vibration_on_cooldown |
is_blocking | 1 while blocking with a shield | 0 / 1 | is_blocking |
critical_hit_eligible | 1 while a hit would crit | 0 / 1 | critical_hit_eligible |
last_damage_amount | your last hit's damage | 0 – 2048 | last_damage_amount |
recent_knockback | your last knockback's strength | 0 – 16 | recent_knockback |
time_airborne | ticks since last on the ground | 0 – 72000 | time_airborne |
sprint_ticks | ticks sprinting without a break | 0 – 72000 | sprint_ticks |
submersion_depth | fluid blocks above your eyes | 0 – 16 | submersion_depth |
glide_ratio | forward ÷ downward speed while gliding (0 otherwise) | 0 – 1000 | glide_ratio |
time_since_last_meal | ticks since you last ate | 0 – 1,000,000,000 | time_since_last_meal |
distance_to_nearest_player | blocks to the closest other player | 0 – 1,000,000 | distance_to_nearest_player |
last_death_distance | blocks to where you last died (the recovery compass's target); −1 if you've never died or died in another dimension | −1 – 100,000,000 | none; in code, Death.distance() |
redstone_power | redstone signal reaching the block at your feet | 0 – 15 | redstone_power |
moon_phase | moon phase | 0 – 7 | moon_phase |
friction | effective slipperiness of the block you move on | 0 – 2 | friction |
is_moving_loudly | 1 while moving loudly | 0 / 1 | is_moving_loudly |
mainhand_cooldown | vanilla cooldown ticks left on the item in your main hand | 0 – 1,000,000,000 | item_on_cooldown |
voice_installed | 1 with Simple Voice Chat connected | 0 / 1 | voice_installed |
voice_mic_disabled | 1 with voice chat disabled | 0 / 1 | voice_mic_disabled |
is_talking | 1 while talking | 0 / 1 | is_talking |
is_whispering_voice | 1 while whispering | 0 / 1 | is_whispering |
in_voice_group | 1 while in a voice group | 0 / 1 | in_voice_group |
is_broadcasting_voice | 1 while broadcasting | 0 / 1 | is_broadcasting_voice |
is_muted_voice | 1 while force-muted | 0 / 1 | is_muted_voice |
is_deafened_voice | 1 while force-deafened | 0 / 1 | is_deafened_voice |
is_downed | 1 while downed (needs Hardcore Revival) | 0 / 1 | is_downed |
Two conditions map to more than one attribute:
- The
velocitycondition's axes are the threevelocity_*attributes. look_angleis split intolook_pitchandlook_yaw.
The voice-chat condition is_whispering is the attribute is_whispering_voice.
These conditions have no attribute: has_attack_target, projectile_age, last_damage_type, is_sleeping, stat, last_hit_was_crit, last_damage_was_crit and has_power_tag, plus the bi-entity conditions.
last_death_distance is the one attribute with no condition. Check it with origins:attribute, for example "comparison": "<=", "compare_to": 16 for "within 16 blocks of your last death". Add a second check with ">=", 0 if "never died" (−1) shouldn't count.
Removed in 2.1.0, along with their conditions: fall_distance, oxygen_remaining, is_gliding, sky_visible, wall_collision, rain_level and thunder_level. In functions, read the vanilla values instead: data get entity @s FallDistance and data get entity @s Air. In powers, use origins:fall_distance, origins:air, origins:fall_flying, origins:exposed_to_sky, origins:collided_horizontally and origins:in_rain.
Drag
too-many-attributes:dragevery living entity, not just playerssettable, not synced from anything
Unlike everything above, this one is a real, ordinary vanilla-style attribute (like generic.movement_speed): it isn't overwritten every tick, so /attribute ... base set and Origins-power attribute modifiers actually stick, and it works on any LivingEntity, not only players.
It's a direct stand-in for vanilla's own fall-drag constant: the fraction of downward speed an entity keeps each tick while falling normally through open air (not swimming, not in lava, not gliding with an elytra, not climbing, and not already moving upward mid-jump). Default 0.98 is that exact vanilla constant, so leaving it alone changes nothing. Vanilla's terminal velocity (how fast a fall eventually caps out) is what this drag value produces, not something set directly - see the table below.
attribute @s too-many-attributes:drag base set 0.9Falling now caps around 0.72 blocks/tick instead of vanilla's 3.92 - a permanent, gentler fall, like a weak Slow Falling that doesn't need the potion effect.
attribute @s too-many-attributes:drag base set 0.995Falling now caps around 15.92 blocks/tick - about four times vanilla's terminal velocity, still with the same gradually-accelerating feel.
drag | Fall caps around | Feel |
|---|---|---|
0.0 | never builds speed | hovers, no fall at all |
0.5 | 0.08 blocks/tick | very heavy air resistance |
0.9 | 0.72 blocks/tick | gentle, slow-falling-like |
0.98 (default) | 3.92 blocks/tick | vanilla |
0.995 | 15.92 blocks/tick | fast, still capped |
1.0 | never caps | true free-fall, accelerates forever |
- Jump height is untouched. This replaces the drag on the way down, never gravity itself, and only ever applies once the entity is already moving downward (or standing still) - the ascending half of a jump always plays out exactly as vanilla.
- It composes with
/attributemodifiers the normal way: add anADD_VALUE/ADD_MULTIPLIED_...modifier instead of setting the base value if you want something temporary (a potion-like power, a status effect from a power) that goes away withmodifier remove. - Slow Falling, Levitation, no-gravity and riding still win. If vanilla's fall formula for that tick doesn't match plain open-air falling, TMA leaves the vanilla result alone rather than fight it.
- Flying and swimming (players) and mobs with custom flight/swim code aren't affected either -
dragonly ever changes the plain gravity-and-drag fall, which those movement modes don't use.
Tip: ← and → turn pages.
Page 8 of 17
Variables
Typed variables you declare in a power and work with using Java expressions. Every entity holding the power gets its own copy, like instances of a Java class. Values are saved with the entity's powers.
| Id | Kind | What it does |
|---|---|---|
variables | power type | declares variables |
variable_bar | power type | links one number variable to Origins' resource system and a HUD bar |
execute | entity and bi-entity action | runs code that changes variables |
modify_variable | entity and bi-entity action | one change, written as JSON like change_resource |
expression | entity and bi-entity condition | checks variables |
Related pages:
- Page 9 puts variables into any Origins field (
with_variables) and any command (/tma with), and reads scoreboards in code. - Page 10 is the full language reference.
- Page 11 covers lists and loops.
A complete example: mana
data/mypack/powers/mana.json declares the variables:
{
"type": "too-many-attributes:variables",
"fields": [
"int mana = 100;",
"int maxMana = 100;",
"boolean casting; // true while a spell is being cast"
]
}data/mypack/powers/mana_regen.json regenerates 5 mana per second:
{
"type": "origins:action_over_time",
"interval": 20,
"entity_action": {
"type": "too-many-attributes:execute",
"code": "mana = Math.min(mana + 5, maxMana);"
}
}data/mypack/powers/fireball.json costs 30 mana, and only works when you have enough:
{
"type": "origins:active_self",
"condition": { "type": "too-many-attributes:expression", "expression": "mana >= 30 && !casting" },
"entity_action": {
"type": "origins:and",
"actions": [
{ "type": "too-many-attributes:execute", "code": "mana -= 30;" },
{ "type": "origins:execute_command", "command": "summon fireball ~ ~1.5 ~" }
]
},
"cooldown": 20
}data/mypack/powers/mana_bar.json shows mana on the HUD:
{
"type": "too-many-attributes:variable_bar",
"variable": "mana",
"min": 0,
"max": 100,
"hud_render": { "should_render": true, "bar_index": 1 }
}One string or an array of lines. fields, code and expression each take either one string or an array of strings. An array is joined with line breaks, so each element is one line of code. You can write one statement per line and put // comments on their own line or after a statement. A // comment ends at the end of its element. Error messages say line N, meaning the Nth element. Statements still need their ;, as in Java.
Variables
too-many-attributes:variablespower type
Declares variables with Java field declarations.
| Field | Type | Default | Description |
|---|---|---|---|
fields | String or array of strings | required | Java field declarations, like int mana = 100, maxMana = mana; |
int mana = 100, maxMana = mana;
double speedBonus = 0.1 * 3;
boolean casting;
String mode = "idle";
long lastCast;- Types:
int,long,float,double,boolean,String. Lists are a separate power (page 11). - Reserved names:
value,indexandsize(used by loops),Scoreboard, and Java keywords can't be variable names. - Without an initializer, a variable starts at Java's default (
0,0.0,false), and aStringstarts at"". - Initializers run once, when the pack loads. They can use literals,
Math.*and the other built-ins, and variables declared earlier in the same power. They can't read scores. Java's assignment rules apply, soint x = 1.5;is a load error; writeint x = (int) 1.5;. - Each holder has their own values. They're saved with the entity's powers, so they survive relogs. Losing the power discards them, and getting it again starts from the initializers. (Suspected: they survive death, because Origins keeps powers through death. Not tested.)
- Changing
fieldsin a pack update: variables that still exist keep their saved values. A number variable whose type changed has its value cast, for example frominttodouble. Anything that can't carry over resets to its initializer. - Names must be unique per entity across all the
variablesandlistpowers it holds. If two powers declare the same name, code that uses it fails withvariable mana is declared by both mypack:a and mypack:b. This is checked when the name is used, because which powers an entity holds is only known then.
Variable bar
too-many-attributes:variable_barpower type
Links one number variable to Origins' resource system. A variable bar is the same kind of power as origins:resource, so everything that works on a resource works on it. Use the bar's power id where Origins asks for a resource.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | Name of an int, long, float or double variable |
min | Integer | required | The value at which the bar is drawn empty |
max | Integer | required | The value at which the bar is drawn full |
hud_render | HUD render | required | How the bar looks, as for origins:resource |
What works through the bar's id:
- The
origins:resourcecondition reads the variable. origins:change_resourceandorigins:modify_resourcewrite the variable.- The HUD bar shows it.
- The
resourcefield of an Origins modifier reads it (page 9). - (Suspected) Origins'
/resourcecommand works on it too.
Unlike origins:resource, nothing is clamped. min and max only set how full the bar looks. With max: 100 and mana = 150, the bar is drawn full, origins:resource still sees 150, and change_resource +1 makes it 151. If you want a limit, clamp in code: mana = Math.min(mana, maxMana);.
- A
longordoublebeyond the int range reads asInteger.MAX_VALUEorMIN_VALUE. - Decimals are cut off: a
doubleof 7.9 reads as 7.
Example: use Origins' own actions on a variable.
{
"type": "origins:active_self",
"key": { "key": "key.origins.secondary_active" },
"entity_action": { "type": "origins:change_resource", "resource": "mypack:mana_bar", "change": 25 }
}mypack:mana_bar is the variable bar from the example above, so this adds 25 to mana.
Execute
too-many-attributes:executeentity action and bi-entity action
Runs statements that change variables.
| Field | Type | Default | Description |
|---|---|---|---|
code | String or array of strings | required | Statements, each ending in ; |
{ "type": "too-many-attributes:execute", "code": "mana -= 30; casting = true; mode = \"fire\";" }- A statement must be an assignment (
=,+=, …), a++/--, or a method call likehistory.add(5). A bare expression such asmana + 5;is an error, as in Java, because you almost certainly meantmana += 5;. There's no
if, loop or local variable. Use?:for choices:mana = casting ? mana - 1 : Math.min(mana + 2, maxMana);- Statements run in order. If one fails while running, the rest are skipped, but earlier changes stay.
- Changed values are synced to the client once, after the code finishes.
Modify variable
too-many-attributes:modify_variableentity action and bi-entity action
One change to one variable, written as JSON in the style of origins:change_resource.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | A variable name. In the bi-entity version also self.name or other.name. |
operation | String | "add" | See the table below |
value | Number, Boolean or String | required | A JSON number or boolean (10, 0.5, true), or an expression as a string or array of lines ("coins / 100"). A String value needs inner quotes: "\"gold\"". It can't change anything. |
operation | Same as the Java statement |
|---|---|
set | x = value |
add | x += value (joins Strings too) |
subtract / multiply / divide / modulo | x -= value / x *= value / x /= value / x %= value |
min / max | x = Math.min(x, value) / x = Math.max(x, value) |
and / or / xor | x &= value / x |= value / x ^= value. On a boolean, xor with true toggles it. |
shift_left / shift_right / unsigned_shift_right | x <<= value / x >>= value / x >>>= value |
{ "type": "too-many-attributes:modify_variable", "variable": "coins", "operation": "add", "value": 10 }
{ "type": "too-many-attributes:modify_variable", "variable": "frozen", "operation": "xor", "value": true }These are exactly coins += 10; and frozen ^= true;.
In the bi-entity version, other. reaches the target. This gives whoever you hit 1% of your coins, exactly other.coins += coins / 100;:
{
"type": "origins:action_on_hit",
"bientity_action": {
"type": "too-many-attributes:modify_variable",
"variable": "other.coins",
"operation": "add",
"value": "coins / 100"
}
}The types follow Java:
setonly widens, sosetwith1.5on anintis an error, just like=.- Every other operation casts the result back to the variable's type, like Java's compound assignment.
minandmaxdo too:maxwith9.9on anintgives9.
Expression
too-many-attributes:expressionentity condition and bi-entity condition
True when a Java expression is true.
| Field | Type | Default | Description |
|---|---|---|---|
expression | String or array of strings | required | One expression that produces a boolean |
{ "type": "too-many-attributes:expression", "expression": "mana >= 30 && mode.equals(\"fire\")" }- Conditions can't change anything.
=,+=,++and list changes are load errors here. - If the expression fails while running (an unknown variable, a missing score), the condition is
falseand the error is logged. - A condition Origins checks on the client, like a
hud_rendercondition, sees the values last synced from the server.
Bi-entity versions
execute, modify_variable and expression also work anywhere a bi-entity action or condition goes, such as origins:action_on_hit, origins:target_action or a bientity_condition.
self.nameis the actor: Origins' first entity, usually the power holder.other.nameis the target: Origins' second entity.- A bare
namemeansself.name.
Example: drain 10 mana from whoever you hit.
{
"type": "origins:action_on_hit",
"bientity_action": {
"type": "too-many-attributes:execute",
"code": "other.mana -= 10; mana = Math.min(mana + 10, maxMana);"
}
}Both entities must hold a variables power that declares the names used. If the target doesn't, the code stops with a "cannot find variable" error in the log. To avoid that, add a bientity_condition that checks the target has the power.
Errors
- Load errors fail that power when the pack loads. The log shows the column and nearby text, e.g.
expected ';' but found 'casting' (at column 16, near "casting = true"). - Errors while running are logged once per distinct message, prefixed
[variables], with the code that caused them. A failing action stops at that statement. A failing condition counts asfalse. - Types are checked when code runs, not when it loads.
mana = "full";loads fine and fails the first time it runs.
Tip: ← and → turn pages.
Page 9 of 17
Variables in Origins fields, commands and scoreboards
Four ways to connect variables to the rest of the game:
| You want to… | Use |
|---|---|
Put a variable into any field of an Origins action or condition: a command, a velocity, an effect duration, a compare_to | with_variables |
| Use variables in chat, command blocks or functions | /tma with |
| Read a scoreboard score in code, for example a value shared by a team | Scoreboard.get |
Read what item is in a slot: a vanilla slot, or one of your origins:inventory powers, for you or the other entity | Item.id and Item.count |
| Feed a number to a power field that's read continuously, such as a modifier | variable_bar + the modifier's resource |
(From source. The placeholder filling is checked by standalone tests; none of this has been tested in-game.)
With variables
too-many-attributes:with_variablesentity action, bi-entity action, entity condition, bi-entity condition
Wraps any Origins action or condition. Any string value inside it can contain ${expression}. Each time it runs, the placeholders are worked out for this entity, and Origins reads the result as if you had typed those values in.
| Field | Type | Default | Description |
|---|---|---|---|
action | Entity action type (or bi-entity action type) | required in the action versions | The action to run |
condition | Entity condition type (or bi-entity condition type) | required in the condition versions | The condition to check |
Placeholder rules:
- The whole string is one placeholder, like
"${speed}". It becomes a real JSON number, boolean or string, so it can fill number and true/false fields. - A placeholder inside text, like
"say I have ${coins} coins", is converted to text the way Java does ("" + coins). Adoubleprints as3.0, alongwithout anL. $${is a literal${.- Inside
${…}goes one expression that doesn't change anything:${coins / 100},${(int) (mana * 20)},${tier.toUpperCase()},${x >= 25 ? "green" : "gray"},${Scoreboard.get("kills")}. String literals inside can contain}. - Bi-entity versions:
${other.name}reads the target and${self.name}the actor. - Inside a
for_each_readloop:${value},${index}and${size}read the current element (page 11).
A number into origins:add_velocity: launch upward with a strength stored in double jumpPower.
{
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:add_velocity",
"y": "${jumpPower * 0.1}",
"space": "world"
}
}A String into origins:execute_command:
{
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:execute_command",
"command": "title @s actionbar {\"text\":\"${rank}: ${mana}/${maxMana} mana\"}"
}
}Numbers into an effect. Cast to int for whole-number fields.
{
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:apply_effect",
"effect": {
"effect": "minecraft:speed",
"duration": "${(int) (mana * 20)}",
"amplifier": "${Math.min(level, 4)}"
}
}
}A condition with a threshold from a variable: true while the food level is below the player's hungerLine.
{
"type": "too-many-attributes:with_variables",
"condition": {
"type": "origins:food_level",
"comparison": "<",
"compare_to": "${hungerLine}"
}
}Bi-entity: damage the target by its own bounty variable.
{
"type": "origins:action_on_hit",
"bientity_action": {
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:target_action",
"action": {
"type": "origins:damage",
"amount": "${(float) other.bounty}",
"damage_type": "minecraft:magic"
}
}
}
}Things to know:
- Some mistakes only show up when the action runs. A syntax error inside
${…}fails the pack load as usual. But Origins can only read the wrapped action after the values are filled in, so a wrong field name, or a value of the wrong type, is logged the first time it runs (prefixed[variables] with_variables:), and that action does nothing. A condition that fails this way counts asfalse. - Cast for whole-number fields, like
duration. (Suspected: a decimal read as a whole number is cut off, e.g.2.9becomes2.) - Quotes in String variables. A String is inserted exactly as it is. If it contains
"and lands inside atellrawJSON text, that JSON breaks. Usename.replace("\"", "'")in the expression if that can happen. - Caching. Each distinct filled-in result is read by Origins once and kept, up to 64 per
with_variables. If a value is different every tick (adoublethat never repeats), the action is re-read every time. That works, just more slowly, so round values in actions that run every tick.
/tma with
For chat, command blocks and .mcfunction files, put tma with <targets> in front of any command:
tma with @s title @s actionbar {"text":"${rank}: ${mana}/${maxMana} mana"}
tma with @a[tag=saver] scoreboard players set @s bank ${(int) coins}
tma with @s tellraw @s {"text":"${history.size()} payouts so far"}- Each target's own values. For every target,
${…}is filled from that target's variables and lists. The command then runs as that target, likeexecute as, so@sis the target. Position, dimension and permission level stay those of whoever ran/tma with. For the target's position, usetma with @a execute at @s run …. - Everything becomes text. A command is always text, so every placeholder uses Java's text conversion.
${d}for adoubleprints3.0, so write${(int) d}where a command needs a whole number.$${is a literal${. - Plain quotes inside
${…}. The placeholder is taken out and replaced before the command runs, so write its quotes plainly, even inside JSON text:title @s actionbar {"text":"${Scoreboard.get("#bank", "team_bank")}"}. (In power JSON, the whole command is a JSON string, so there every"is written\"as usual.) - All or nothing. Every target is filled in before any command runs. If one can't be (an unknown variable, a wrong type, a typo inside
${…}), nothing runs and/tma withfails with the reason. - Result. The sum of the commands' results, so
execute store result score @s x run tma with @s …works for one target. - Permission level 2, like the other
/tmacommands. Functions and command blocks have it.
Scoreboard
Code can read scores. This works anywhere code runs: execute code, expression conditions, modify_variable values, for_each code and ${…}.
| Call | Returns | |
|---|---|---|
Scoreboard.get(objective) | int | this entity's score in objective |
Scoreboard.get(holder, objective) | int | the score of any holder: a player name, or a fake name like #bank |
Scoreboard.has(objective) | boolean | whether this entity has a score in objective |
Scoreboard.has(holder, objective) | boolean | whether holder has a score in objective |
teamBank = Scoreboard.get("#bank", "team_bank");
kills = Scoreboard.get("kills");
bonus = Scoreboard.has("#event", "active") ? Scoreboard.get("#event", "active") * 10 : 0;- A missing score is an error, as with vanilla's
/scoreboard players get: an action stops there, and a condition isfalse. An unknown objective is an error too. Check withScoreboard.hasfirst when a score may not be set. - "This entity" is the power holder (in bi-entity code, the actor). It uses the entity's scoreboard name: a player's name, or the UUID for other entities. The target of bi-entity code can't be read by name this way.
- Holders are exact names. Selectors like
@sor@pdon't work here. - It only reads. To write a score, run a command with the value:
/tma withorwith_variables+execute_command, as below. - Not in field initializers: they run when the pack loads, when there's no world to read from.
- (Suspected) The client may only know objectives shown in a display slot, so conditions that Origins checks on the client may fail to read scores.
Shared values through scoreboards. A team bank kept on the fake player #bank:
{
"type": "origins:and",
"actions": [
{
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:execute_command",
"command": "scoreboard players add #bank team_bank ${Math.min(coins, 10)}"
}
},
{ "type": "too-many-attributes:execute", "code": "coins -= Math.min(coins, 10);" }
]
}This deposits up to 10 of your coins. Anyone can check the shared total with Scoreboard.get("#bank", "team_bank"), for example in an expression condition or a HUD message.
Attributes into variables. Store an attribute in a score, then read it:
execute as @a store result score @s sprint run attribute @s too-many-attributes:sprint_ticks getlongestSprint = Math.max(longestSprint, Scoreboard.get("sprint"));Power fields: variable bar and modifier resource
Power fields, like a modify_jump modifier, are read when the power is granted, so with_variables can't reach them. For a number a power uses continuously, link the variable with a variable_bar and put the bar's id in the modifier's resource field. The Origins docs describe that field as: "If specified, the value of this power will be used instead of the value specified in the value field."
Example: bonus damage equal to an int rage variable, linked through a variable bar with the id mypack:rage_bar:
{
"type": "origins:modify_damage_dealt",
"modifier": {
"operation": "add_base_early",
"value": 0,
"resource": "mypack:rage_bar"
}
}This works for any power that takes an Origins modifier: modify_damage_dealt, modify_damage_taken, modify_jump, modify_break_speed, conditioned_attribute, and so on. A bar is a whole number, so store the value scaled if you need fractions (e.g. int speedPercent), and pick an operation to match. (From source: Origins' modifier reads the bar's value. Not tested in-game.)
Tip: ← and → turn pages.
Page 10 of 17
Language reference
Variable code is Java's expression syntax, with Java's precedence, associativity, number promotion and overflow. This page lists everything the language has.
Where code goes
| Field | Contains | Can change things? |
|---|---|---|
fields of variables | field declarations | only its own initial values |
code of execute | statements | yes |
code of for_each | statements | yes, including value |
expression of expression | one boolean expression | no |
value of modify_variable | one expression | no |
${…} in with_variables and /tma with | one expression | no |
Names
| Name | Means |
|---|---|
mana | this entity's variable (the actor, in bi-entity code) |
self.mana | the same, written out |
other.mana | the target's variable. Only in bi-entity code. |
Scoreboard, Item, Death, Stat, Global | built-in names; they can't be variable names |
value, index, size | the current element of the innermost loop (page 11) |
history.size(), … | a list's methods (page 11) |
Literals
42, 0xFF, 1_000_000, 10L, 1.5, 1.5f, 2e3, 1d, true, false, and "text" with the escapes \n \t \" \\ \uXXXX.
Comments: // and /* */.
Operators
Highest precedence first:
| Operators | Notes |
|---|---|
x++ x-- | variables only |
++x --x +x -x !x ~x (type) x | |
* / % | whole-number division rounds toward zero; dividing an int or long by zero is an error |
+ - | + joins text when either side is a String |
<< >> >>> | int/long |
< <= > >= | numbers |
== != | numbers, booleans, Strings |
& ^ | | bitwise on int/long, logical on booleans |
&& || | stop early when the answer is already known |
?: | |
= += -= *= /= %= &= |= ^= <<= >>= >>>= | actions only |
Types and conversions
As in Java:
- Mixed arithmetic promotes: int, then long, then float, then double.
5 / 2is2, and5 / 2.0is2.5. - int and long overflow wraps around.
=only widens.int x = someDouble;is an error; writex = (int) someDouble;.- Compound assignment casts for you, so
x += 1.5on anintis allowed. - Casts between number types behave like Java's. Booleans and Strings can't be cast to or from numbers. Use
String.valueOf(x),"" + xorInteger.parseInt(s).
Built-ins
| Group | Members |
|---|---|
Math. | abs max min clamp round floor ceil rint signum sqrt cbrt pow exp log log10 sin cos tan asin acos atan atan2 hypot toRadians toDegrees floorDiv floorMod random, PI, E |
Integer., Long., Float., Double. | MAX_VALUE, MIN_VALUE, parseInt(s) / parseLong / parseFloat / parseDouble |
Double. | POSITIVE_INFINITY, NEGATIVE_INFINITY, NaN, isNaN(x), isInfinite(x) |
Boolean. / String. | Boolean.parseBoolean(s), String.valueOf(x) |
Scoreboard. | get(objective), get(holder, objective), has(objective), has(holder, objective): see page 9 |
Item. | id(slot), id(power, slot), count(slot), count(power, slot): what's in a vanilla slot or a slot of an origins:inventory power. cooldown(item or slot): vanilla cooldown ticks left. Each takes an optional first argument self or other. See page 14 |
Death. | has(), x(), y(), z(), dimension(), distance(): where the player last died. See Death below |
Stat. | get(stat), get(self or other, stat): a vanilla statistic. See Stat below |
Global. | has(key), get(key), set(key, value), delete(key), bind(key, self or other), lock(key, owner), unlock(key), list(key), list(key, type): the global store. See page 12 |
| String methods | length() isEmpty() isBlank() equals(x) equalsIgnoreCase(s) contains(s) startsWith(s) endsWith(s) indexOf(s) lastIndexOf(s) compareTo(s) concat(s) toUpperCase() toLowerCase() trim() strip() substring(i) substring(i, j) replace(a, b) repeat(n) |
| List methods | size() isEmpty() get(i) contains(v) indexOf(v) set(i, v) add(v) add(i, v) remove(x) clear(): see page 11 |
When a method has several versions, the one javac would pick is used:
Math.abs(int)staysintMath.max(int, long)islongMath.round(double)islongMath.round(float)isint
Death
The player's last death location, the one vanilla keeps for the recovery compass. Each method takes an optional self or other (bi-entity code only), like Death.distance(other).
| Method | Type | Value |
|---|---|---|
Death.has() | boolean | whether there's a death location. Always false for non-players. |
Death.x(), Death.y(), Death.z() | int | the block where they died. An error without a death location, so check Death.has() first. |
Death.dimension() | String | the dimension they died in, like minecraft:the_nether. Same error rule. |
Death.distance() | double | blocks from the entity to the centre of that block. -1 when there's no death location or it's in another dimension; never an error. |
- The location survives relogging and restarts (vanilla saves it), and only a real death changes it.
- The same distance is the
last_death_distanceattribute.
Example: execute code that stores a hint in a String variable: grave_hint = Death.has() ? "grave at " + Death.x() + " " + Death.z() : "no grave";
Stat
Stat.get(stat) is the player's value for a vanilla statistic, an int. Stat.get(other, stat) reads the other entity in bi-entity code.
The name is written the way scoreboard criteria write it: stat type and id, each with . instead of :, joined by a :. For example:
"minecraft.custom:minecraft.jump""minecraft.mined:minecraft.stone""minecraft.killed:minecraft.zombie"- A name that isn't a statistic is an error when the code runs.
- Non-players and the client read
0. Stats only exist on the server. - Units are vanilla's: centimetres for distances, ticks for time. See the
statcondition for the list of types.
Example: blocks walked per hour played, as a double:
Stat.get("minecraft.custom:minecraft.walk_one_cm") / 100.0 / (Stat.get("minecraft.custom:minecraft.play_time") / 72000.0)Differences from Java
==on Strings compares the text, like.equals(). In Java it checks whether they're the same object.- No
null. An empty String is"". - No
char,byteorshort. - No control flow (
if,for,while,return), no local variables, nonew, and no methods of your own. Use?:for choices and the loop actions on page 11 for lists. - Types are checked when code runs, not when it loads. Syntax errors, unknown methods, assigning in a condition, and field initializers fail when the pack loads. A type error like
mana = "full";is only reported when that code runs. - The two branches of
?:keep their own types. Java would promotec ? 1 : 2.0todoubleeither way. toUpperCase()andtoLowerCase()ignore the system language.- No octal numbers:
010is an error rather than 8.
Errors
- Load errors fail the power when the pack loads, with the line, column and nearby text:
expected ';' but found 'casting' (at column 16, near "casting = true"). - Errors while running are logged once per distinct message, prefixed
[variables], with the code that caused them. An action stops at the failing statement, and earlier changes stay. A condition counts asfalse.
Tip: ← and → turn pages.
Page 11 of 17
Lists and loops
Typed, resizable lists you declare in a power, use from code with Java's List methods, and loop over with two "for each" actions. Lists sit beside variables, not inside them: a variable is never a list.
| Id | Kind | What it does |
|---|---|---|
list | power type | declares one list |
power_list | power type | a read-only list of the holder's current power ids |
item_list | power type | a read-only list of the item ids in one of the holder's origins:inventory powers |
for_each | entity and bi-entity action | runs code for each element; value can be changed |
for_each_read | entity and bi-entity action | runs any action for each element; nothing can change the list |
List
too-many-attributes:listpower type
Declares one list. Each holder has their own list, saved with their powers and synced to the client when it changes.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | The name code uses. Same rules as variable names, and unique on the entity across all variables and list powers. |
element_type | String | required | int, long, float, double, boolean or String. Every element has this type. |
initial | Array | [] | Starting elements, e.g. [1, 2, 3] or ["a", "b"]. Numbers follow Java's rules (1 is an int, 1.5 a double) and convert like =. |
max_size | Integer | 1024 | The most elements the list can hold |
when_full | String | "error" | What add does on a full list: "error" fails, and "drop_oldest" removes element 0 first, which makes a rolling log. |
{
"type": "too-many-attributes:list",
"variable": "history",
"element_type": "long",
"max_size": 12,
"when_full": "drop_oldest"
}This keeps the last 12 values added to history.
Limitation: Origins builds a power when it's granted, not when the pack loads. So an initial element that doesn't fit element_type (like 1.5 in an int list) can only be caught then. It's skipped with a warning in the log instead of failing. The list name and the other fields are checked when the pack loads.
Using a list in code
Lists have the methods of Java's java.util.List. They work in execute code, expression conditions, modify_variable values and ${…}, and other.history works in the bi-entity versions.
| Method | Returns | Notes |
|---|---|---|
history.size() | int | |
history.isEmpty() | boolean | |
history.get(i) | the element | error if i is outside 0 … size()-1 |
history.contains(v) / history.indexOf(v) | boolean / int | indexOf is -1 when missing |
history.set(i, v) | the old element | changes the list |
history.add(v) | true | changes the list; follows when_full |
history.add(i, v) | nothing | inserts at i; changes the list |
history.remove(i) | the removed element | an int argument removes by position |
history.remove(v) | boolean | any other argument removes the first equal element |
history.clear() | nothing | changes the list |
history.add(coins - lastBalance);
best = history.isEmpty() ? 0 : Math.max(best, history.get(history.size() - 1));- Methods that change the list (
set,add,remove,clear) are load errors in conditions,modify_variablevalues and${…}, just like assignments. - A list isn't a value:
x = history;orhistory + 1fails with "history is a list". Use its methods. - Using a result that doesn't exist (
x = history.clear();) fails, as in Java.
Loops
Inside a loop, three reserved names describe the innermost loop:
| Name | Type | |
|---|---|---|
value | the element type | the current element |
index | int | its position, starting at 0 |
size | int | the list's size |
indexandsizeare always read-only.valuecan be changed only directly in afor_eachcode block. Assigning it anywhere else is a load error.value,indexandsizecan't be used as variable names. Outside a loop they fail with "value only exists inside …".- While a loop runs, its list can't grow or shrink:
add,removeandclearon it fail, like Java'sConcurrentModificationException. Other lists and variables can change freely.
For each
too-many-attributes:for_eachentity action and bi-entity action
Runs code once for each element of a list. Changing value changes that element.
| Field | Type | Default | Description |
|---|---|---|---|
list | String | required | The list: cooldowns, or other.cooldowns in the bi-entity version |
code | String or array of strings | required | Statements, run once per element |
Example: count every ability cooldown down by one each tick.
{
"type": "origins:action_over_time",
"interval": 1,
"entity_action": {
"type": "too-many-attributes:for_each",
"list": "cooldowns",
"code": "value = Math.max(value - 1, 0);"
}
}The code can also read and change ordinary variables, for example total += value;, or ready += value == 0 ? 1 : 0; to count how many are ready. An error in the code stops the loop, like an exception in a Java for loop; changes made before it stay.
For each read
too-many-attributes:for_each_readentity action and bi-entity action
Runs any action once for each element of a list. Nothing inside can change the list.
| Field | Type | Default | Description |
|---|---|---|---|
list | String | required | The list: history, or other.history in the bi-entity version |
action | Entity action type | required in the entity version | Run once per element |
bientity_action | Bi-entity action type | required in the bi-entity version | Run once per element |
Inside the action, everything from TMA can read value, index and size: expression conditions, execute code, modify_variable values and ${…} in with_variables. Other variables can still change, so a running total works.
Example: a puff of particles per payout, green for big ones.
{
"type": "too-many-attributes:for_each_read",
"list": "history",
"action": {
"type": "origins:if_else",
"condition": { "type": "too-many-attributes:expression", "expression": "value >= 25" },
"if_action": { "type": "origins:execute_command", "command": "particle minecraft:happy_villager ~ ~2.2 ~ 0.4 0.2 0.4 0 4" },
"else_action": { "type": "origins:execute_command", "command": "particle minecraft:smoke ~ ~2.2 ~ 0.4 0.2 0.4 0 4" }
}
}Example: print each element in chat.
{
"type": "too-many-attributes:for_each_read",
"list": "history",
"action": {
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:execute_command",
"command": "tellraw @s {\"text\":\"#${index + 1}: ${value}\"}"
}
}
}Nesting
A for_each_read action can contain another loop, over another list or the same one. value, index and size always mean the innermost loop, so an outer element isn't reachable from inside an inner loop. Copy it into a variable first if you need it.
Power list
too-many-attributes:power_listpower type
A read-only list of String power ids: the powers the holder has at the moment it's read. Nothing is stored. Each code run takes one fresh snapshot, and each loop takes one when it starts, so a power granted or removed a moment ago shows up the next time. The ids are sorted alphabetically.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | The name code uses. Same rules as list. |
include_hidden | Boolean | false | Also list hidden powers. (Suspected: parts of origins:multiple powers are usually hidden, so the default is roughly what a player sees.) |
only_active | Boolean | false | Leave out powers whose own condition is false right now |
tag | String | none | Only powers in this power tag. Without a namespace it gets this power's own. |
source | Identifier | none | Only powers granted by this source. Origins' /power sources <player> <power> shows a power's sources. |
It includes its own power unless that power has "hidden": true:
{ "type": "too-many-attributes:power_list", "hidden": true, "variable": "powers" }Everything from Using a list in code that doesn't change the list works on it, and so does for_each_read:
{ "type": "too-many-attributes:expression", "expression": "powers.contains(\"origins:elytra\")" }{
"type": "too-many-attributes:for_each_read",
"list": "powers",
"action": {
"type": "too-many-attributes:modify_variable",
"variable": "fromOrigins",
"operation": "add",
"value": "value.startsWith(\"origins:\") ? 1 : 0"
}
}Anything that would change it (add, set, remove, clear, or a for_each that assigns value) fails with "powers is read-only". It fails when the code runs, not when the pack loads, because a name isn't known to be a power list until then.
Item list
too-many-attributes:item_listpower type
A read-only list of what's in one of the holder's origins:inventory powers, as it is at the moment code reads it. One element per slot, so index is the slot number and get(3) is container.3. Nothing is stored.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | The name code uses. Same rules as list. |
power | Identifier | required | The origins:inventory power to read |
counts | Boolean | false | false lists item ids as Strings; true lists stack sizes as ints |
{ "type": "too-many-attributes:item_list", "hidden": true, "variable": "satchel", "power": "mypack:satchel" }- An empty slot is
minecraft:air, the item vanilla actually keeps there, so the list length always matches the power's size. - If the holder doesn't have that power, or it isn't an
origins:inventorypower, the list is empty and the reason is logged once. - Like every computed list it's read-only:
add,set,removeandclearfail when the code runs. Use the actions on page 14 to change items.
Example: count how many slots hold logs.
{
"type": "too-many-attributes:for_each_read",
"list": "satchel",
"action": {
"type": "too-many-attributes:modify_variable",
"variable": "logs",
"operation": "add",
"value": "value.endsWith(\"_log\") ? 1 : 0"
}
}Example: is the satchel completely empty?
{
"type": "too-many-attributes:expression",
"expression": "satchel.indexOf(\"minecraft:air\") == 0 && satchel.contains(\"minecraft:air\") && satchel.size() == 9"
}A simpler version of the same check, with a counts list named satchelCounts: satchelCounts.contains(1) == false. For "what is in slot 3" on its own, Item.id is more direct than a list.
Differences from Java
remove(int)always removes by position. To remove anintelement by value, useremove(indexOf(v)).add/setwiden like=:3goes into adoublelist as3.0. Java's generics would reject it.contains/indexOfcompare like==, so3matches3L. Java'sequalswouldn't.- No generics syntax, no
new ArrayList<>(), no lists of lists, and lists can't be assigned or passed around. A list only exists as a power. - Unlike Java's for-each,
for_eachwritesvalueback into the list. That's its purpose.
Tip: ← and → turn pages.
Page 12 of 17
Global store
Variables and lists belong to one entity: every holder has its own copy. The global store holds values that belong to no entity. Any number of entities can read and write the same one: a faction's score, a world-puzzle flag, a bond between two particular players.
- Addressed by a key like
mypack:bond/alice. A key without a namespace getstoo-many-attributes:, so"score"istoo-many-attributes:score. Keys use lowercase letters, digits and_ - . /. - Typed like variables:
int,long,float,double,booleanorString, or a list of one of those. A key's type is fixed when it's created, and later writes follow Java's assignment rules. - Saved with the world (
data/too-many-attributes_global.dat), like scoreboards. It isn't per dimension and doesn't reset on its own. Put the dimension or the day in the key if you need that. - Server only. The client has no globals, so a client-checked condition such as a
hud_rendercondition can't read them (it counts as false and logs an error).
There are four ways in:
| Way | Use it for |
|---|---|
Global.* in code | any key, including keys built while the code runs |
global_variables power | fixed, declared fields that every holder uses by plain name |
modify_global and delete_global actions | JSON, like modify_variable |
/tma global | looking at and fixing the store as an operator |
In code
Global works everywhere code runs: execute, expression, modify_variable values, for_each, and ${…} in with_variables and /tma with.
| Method | Returns | |
|---|---|---|
Global.has(key) | boolean | whether the key exists |
Global.get(key) | the value | an error if the key doesn't exist or is a list |
Global.set(key, value) | the value | creates the key with the value's type, or sets an existing one |
Global.delete(key) | boolean | removes the key; true if it existed |
Global.bind(key, self) / Global.bind(key, other) | ties the key to an entity; see cleanup | |
Global.lock(key, owner) / Global.unlock(key) | see locking | |
Global.list(key) | a list | an existing global list; use it with list methods: Global.list(key).size() |
Global.list(key, type) | a list | the same, but creates an empty list of type ("int", "String", …) if it's missing |
set,delete,bind,lock,unlockandlist(key, type)change the store, so they only work in actions (execute,for_each), not in conditions ormodify_variablevalues. The same goes for list methods that change a list.- Keys are Strings, so they can be built from variables:
Global.set("mypack:kills/" + team, Global.get("mypack:kills/" + team) + 1). Globalis a reserved name and can't be a variable name.- Global lists hold at most 1024 elements. Adding past that is an error.
Example: count every zombie kill on the server.
{
"type": "origins:self_action_on_kill",
"target_condition": { "type": "origins:entity_type", "entity_type": "minecraft:zombie" },
"entity_action": {
"type": "too-many-attributes:execute",
"code": "Global.set(\"mypack:zombie_kills\", Global.has(\"mypack:zombie_kills\") ? Global.get(\"mypack:zombie_kills\") + 1 : 1);"
},
"cooldown": 1
}Loops: for_each and for_each_read take a global list as their list:
{ "type": "too-many-attributes:for_each_read", "list": "Global.list(\"mypack:members\")", "action": { "type": "origins:heal", "amount": 1 } }Global variables
too-many-attributes:global_variablespower type
Declares fields exactly like variables, but every holder shares one copy in the global store. Code on a holder uses them by plain name, the same as ordinary variables.
| Field | Type | Default | Description |
|---|---|---|---|
fields | String or array of Strings | required | Java field declarations: int faction_score = 0; boolean gate_open; |
lock | Boolean | false | When true, keys this power creates are locked to its namespace. |
- The key for field
namein powermypack:whateverismypack:name, in lowercase. Any power in themypacknamespace declaring the same name shares it.Global.get("mypack:name")reads it from anywhere. - The initial value is used only when the key doesn't exist yet. After that, the stored value wins.
- If the key already exists with another type, using the field is an error.
- A name can't be declared by both a
variablesand aglobal_variablespower the entity holds. variable_barand client-side checks don't work with global fields, since the client doesn't have them.
Example: a shared faction score. Every member has this power, and a member's kill adds to everyone's total.
{
"type": "too-many-attributes:global_variables",
"fields": "int faction_score = 0;"
}{
"type": "origins:self_action_on_kill",
"entity_action": { "type": "too-many-attributes:execute", "code": "faction_score += 1;" },
"cooldown": 1
}Modify global
too-many-attributes:modify_globalentity action and bi-entity action
The JSON form of changing one global, like modify_variable.
| Field | Type | Default | Description |
|---|---|---|---|
key | String | required | The key. |
operation | String | "set" | set, add, subtract, multiply, divide, modulo, min, max, and, or, xor, shift_left, shift_right, unsigned_shift_right |
value | String | required | An expression, like "1" or "coins / 10". It can read variables and globals. |
bind | String | "none" | After the change, bind the key to self, other (bi-entity only), or both. |
setcreates a missing key. Any other operation needs the key to exist already.
Delete global
too-many-attributes:delete_globalentity action and bi-entity action
| Field | Type | Default | Description |
|---|---|---|---|
key | String | required | The key to remove. Nothing happens if it doesn't exist. |
Example: turn on a world flag the first time anyone enters the Nether.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": { "type": "origins:dimension", "dimension": "minecraft:the_nether" },
"entity_action": { "type": "too-many-attributes:modify_global", "key": "mypack:nether_opened", "value": "true" }
}Locking
By default any pack can change any key. That's what you want when each origin on a server is unique. Locking helps when one server runs several copies of the same origin, or several packs, that shouldn't touch each other's state.
- A locked key has an owner: a namespace (
mypack) or a single power id (mypack:bond). - Only an entity that holds a power in that namespace, or that exact power, can change the key:
set,delete,bind, list changes,lockandunlock. Anything else gets an error naming the owner. - Reading is never locked.
- Lock with
Global.lock(key, owner). The entity doing it must itself hold the owner.global_variableswith"lock": truelocks the keys it creates to its namespace. /tma globalcommands ignore locks, so an operator can always fix things.
Cleanup
Globals stay until something deletes them. Keys don't expire on their own, because an origin might expect a value to still be there. There are three ways they go away:
- Explicit delete:
Global.delete,delete_global, or/tma global delete. Binding.
Global.bind(key, self)(orbindonmodify_global) ties the key to an entity. A key can be bound to several entities, and it's deleted once all of them are gone:- a mob or other non-player is gone when it's killed or removed. Unloading with its chunk doesn't count.
- a player is gone when they've been offline longer than the gamerule
tmaPlayerExpiryDays(real days, default30,0= never). Dying doesn't count.
- Admin pruning:
/tma global prune, which only runs when an operator types it.
Example: a bond between two players that disappears once both have been away for good. See the cookbook.
/tma global
tma global get <key>
tma global set <key> <value>
tma global delete <key>
tma global list [<prefix>]
tma global prune <prefix>
tma global prune --older-than <ticks>
tma global lock <key> <owner>
tma global unlock <key>| Subcommand | What it does | Result |
|---|---|---|
get | prints the value | the value if it's a whole number, 1/0 for booleans, the size for a list, otherwise 1 |
set | sets or creates a key. The value is a Java literal: 5, 2.5, 10L, true, "text" | the new value if it's a number, otherwise 1 |
delete | removes a key | 1 |
list | every key, or those starting with <prefix>, with type, value, how long since it was written, its lock owner and how many entities it's bound to | the number listed |
prune <prefix> | deletes every key starting with <prefix> | how many were deleted |
prune --older-than <ticks> | deletes every key not written for at least that many ticks | how many were deleted |
lock / unlock | locks a key to an owner, or removes the lock | 1 |
- Commands need permission level 2 and ignore locks.
- A key or prefix without a namespace gets
too-many-attributes:.
Tip: ← and → turn pages.
Page 13 of 17
Voice chat
TMA works with Simple Voice Chat (SVC) when it's installed. SVC is optional: without it, TMA loads normally, and the voice conditions are simply false.
| Id | Kind | Needs SVC? |
|---|---|---|
voice_installed | entity condition | yes |
voice_mic_disabled | entity condition | yes |
is_talking | entity condition | yes |
is_whispering | entity condition | yes |
in_voice_group | entity condition | yes |
in_same_voice_group | bi-entity condition | yes |
in_voice_range_of | bi-entity condition | yes |
is_broadcasting_voice | entity condition | no (it reads TMA's own state) |
is_muted_voice | entity condition | no (it reads TMA's own state) |
is_deafened_voice | entity condition | no (it reads TMA's own state) |
start_broadcast_voice | entity action | to be heard, yes |
stop_broadcast_voice | entity action | no |
/tmavc | command | to have an effect, yes |
How it works:
- What TMA knows. TMA listens to SVC on the server: who is connected, who sent microphone audio recently, and who is in which group. It doesn't change anyone's SVC settings or group membership.
- Broadcast sends a player's voice, as non-positional audio, straight to chosen listeners anywhere on the server. Listeners still hear normal nearby and group audio as well.
- Forced mute drops a player's microphone audio before SVC passes it on, so nobody hears them: not nearby, not in groups, not through a broadcast.
- Forced deafen stops all voice audio from reaching a player.
- Nothing is saved. Broadcasts, mutes and deafens end when the server restarts.
All of these are also attributes (is_whispering is is_whispering_voice), so client-side checks can use origins:attribute.
Voice installed
too-many-attributes:voice_installedentity condition
True when the player has SVC installed and connected to the server's voice chat.
No fields.
Example: a reminder for players without voice chat.
{
"type": "origins:action_over_time",
"interval": 600,
"condition": { "type": "too-many-attributes:voice_installed", "inverted": true },
"entity_action": {
"type": "origins:execute_command",
"command": "tellraw @s {\"text\":\"This origin's powers use Simple Voice Chat. Install it to use them.\",\"color\":\"yellow\"}"
}
}Voice mic disabled
too-many-attributes:voice_mic_disabledentity condition
True when the player has turned voice chat off in their SVC client (SVC's "disabled" state).
No fields.
Is talking
too-many-attributes:is_talkingentity condition
True while the player is talking: the server got microphone audio from them within the last 0.75 seconds.
No fields.
- A player who is force-muted with
/tmavc mutedoesn't count as talking.
Example: music notes while you speak.
{
"type": "origins:action_over_time",
"interval": 5,
"condition": { "type": "too-many-attributes:is_talking" },
"entity_action": {
"type": "origins:execute_command",
"command": "particle minecraft:note ~ ~2.3 ~ 0 0 0 1 1"
}
}Is whispering
too-many-attributes:is_whisperingentity condition
True while the player is talking (above) and their latest audio was a whisper.
No fields.
Example:
{
"type": "origins:invisibility",
"condition": { "type": "too-many-attributes:is_whispering" }
}While you whisper, you're invisible.
In voice group
too-many-attributes:in_voice_groupentity condition
True while the player is in an SVC group.
No fields.
In same voice group
too-many-attributes:in_same_voice_groupbi-entity condition
True when the actor and the target are in the same SVC group.
No fields.
Example: no friendly fire within a voice group.
{
"type": "origins:modify_damage_dealt",
"bientity_condition": { "type": "too-many-attributes:in_same_voice_group" },
"modifier": { "operation": "multiply_total_multiplicative", "value": -1 }
}Your hits deal no damage to players in your voice group.
In voice range of
too-many-attributes:in_voice_range_ofbi-entity condition
True when the actor and the target are within voice range of each other.
| Field | Type | Default | Description |
|---|---|---|---|
max_distance | Double | -1 | The range in blocks. Any negative value uses the voice distance set in SVC's server config. |
- False without SVC, whatever
max_distanceis.
Is broadcasting voice
too-many-attributes:is_broadcasting_voiceentity condition
True while the entity is broadcasting, through start_broadcast_voice or /tmavc broadcast start. This reads TMA's own state, so it works without SVC too, for example for particles.
No fields.
Example:
{
"type": "origins:action_over_time",
"interval": 10,
"condition": { "type": "too-many-attributes:is_broadcasting_voice" },
"entity_action": {
"type": "origins:execute_command",
"command": "particle minecraft:electric_spark ~ ~2.2 ~ 0.2 0.2 0.2 0 4"
}
}Is muted voice
too-many-attributes:is_muted_voiceentity condition
True while the player is force-muted with /tmavc mute start. It isn't about the player's own mic toggle.
No fields.
Is deafened voice
too-many-attributes:is_deafened_voiceentity condition
True while the player is force-deafened with /tmavc deafen start.
No fields.
Start broadcast voice
too-many-attributes:start_broadcast_voiceentity action
Starts broadcasting the entity's voice to every online player who passes listeners. It replaces any broadcast already running.
| Field | Type | Default | Description |
|---|---|---|---|
listeners | Bi-entity condition type | required | Checked for each online player, each time audio is sent. The actor is the broadcaster and the target is the possible listener. |
ticks | Integer | -1 | How long it lasts. 0 or less means until stop_broadcast_voice. |
- Listeners hear the broadcast in addition to normal nearby audio, so a listener standing next to the broadcaster hears them twice. Use
in_voice_range_ofinverted to leave out players who can already hear them. - The broadcaster never hears themselves, and force-deafened listeners hear nothing.
- Because
listenersis checked as audio arrives, players who start or stop matching it are added or dropped straight away.
Example: a radio. Everyone with the mypack:radio power hears you for 10 seconds, unless they're close enough to hear you anyway.
{
"type": "origins:active_self",
"key": { "key": "key.origins.secondary_active" },
"cooldown": 400,
"hud_render": { "should_render": true },
"entity_action": {
"type": "too-many-attributes:start_broadcast_voice",
"ticks": 200,
"listeners": {
"type": "origins:and",
"conditions": [
{ "type": "origins:target_condition", "condition": { "type": "origins:power", "power": "mypack:radio" } },
{ "type": "too-many-attributes:in_voice_range_of", "inverted": true }
]
}
}
}Stop broadcast voice
too-many-attributes:stop_broadcast_voiceentity action
Ends the entity's broadcast, if it has one.
No fields.
/tmavc
Voice controls for admins and testers. They work without any power, and need permission level 2.
tmavc broadcast start [<listeners>]
tmavc broadcast stop
tmavc mute start <targets>
tmavc mute stop <targets>
tmavc deafen start <targets>
tmavc deafen stop <targets>| Subcommand | What it does |
|---|---|
broadcast start | You broadcast to every online player, or only to the <listeners> players. It lasts until broadcast stop. Must be run by a player. |
broadcast stop | Ends your broadcast |
mute start / stop | Force-mutes or unmutes the target players |
deafen start / stop | Force-deafens or undeafens the target players |
mute and deafen return the number of targets, and broadcast returns 1.
Tip: ← and → turn pages.
Page 14 of 17
Power inventories
An origins:inventory power keeps its items inside Origins' power data, not in the player's NBT. There are two ways to reach them:
| You want to… | Use | Reaches |
|---|---|---|
| Check or change your own power inventory from power JSON | Origins' own types, below | only the entity running the condition or action |
| Read it from a function, or copy/move items between players, or between a power and a normal slot | /tma powerinv (TMA) | any entity, any power, any vanilla slot |
Vanilla /item and /data can't see power inventories. Use /tma powerinv instead.
Slots: a power's slots are container.0 … container.<size-1>. The size depends on the power's container_type: dropper has 9 (the default), hopper 5, chest 27. This is the naming Origins' item_slot docs recommend for power inventories.
From power JSON (Origins, no TMA needed)
(From source: Apoli 2.9.2, 2026-09-11. Not tested in-game.)
The inventory is named by its power id. Four Origins types accept it:
| Type | Field that selects power inventories | Plus |
|---|---|---|
origins:inventory (entity condition) | "inventory_types": ["power"], an array | "power": "<id>" |
origins:modify_inventory (entity action) | "inventory_type": "power", a single value | "power": "<id>" |
origins:replace_inventory (entity action) | "inventory_type": "power" | "power": "<id>" |
origins:drop_inventory (entity action) | "inventory_type": "power" | "power": "<id>" |
Catches:
- You need both fields. With
powerbut noinventory_type(s), the player's normal inventory is used. Withinventory_type(s)but nopower, no power inventory is used. It does not fall back to "all power inventories". - Slots past the power's size are skipped without a warning.
- Only your own inventory. All four types act only on the entity they run on, with no bi-entity version, so power JSON can't move items between two players. Use
/tma powerinvfor that.
Example: does slot 0 of mypack:satchel hold at least 1 item?
{
"type": "origins:inventory",
"inventory_types": ["power"],
"power": "mypack:satchel",
"slot": "container.0",
"comparison": ">=",
"compare_to": 1
}Example: use up one item from each of the first three slots.
{
"type": "origins:modify_inventory",
"inventory_type": "power",
"power": "mypack:satchel",
"slots": ["container.0", "container.1", "container.2"],
"item_action": { "type": "origins:consume", "amount": 1 }
}From commands: /tma powerinv
Needs permission level 2, like /item.
tma powerinv get <target> <power> <slot>
tma powerinv id <target> <power> <slot>
tma powerinv set <target> <power> <slot> <item> [count]
tma powerinv clear <target> <power> [slot]
tma powerinv copy <from> <to>
tma powerinv move <from> <to><from> and <to> are each one of:
power <target> <power> <slot>: a slot in a power inventory, e.g.power @s mypack:satchel container.3entity <target> <slot>: any vanilla slot, e.g.entity @s hotbar.0orentity @s enderchest.5
<target> must be exactly one entity. It must hold the power, and the power must be an origins:inventory power; otherwise the command fails with a message saying which.
| Subcommand | What it does | Result (for execute store result) |
|---|---|---|
get | Shows the stack in the slot | the item count, or 0 if empty |
id | Prints the item's id on its own, as plain text: minecraft:spruce_log, or minecraft:air for an empty slot | the item's number in the item registry |
set | Replaces the slot with a new stack | the count set |
clear | Empties one slot, or the whole power if no slot is given | items removed (one slot) or stacks removed (whole power) |
copy | Copies the source stack over the destination. Overwrites anything already there, and the source keeps its item. | the count copied |
move | Moves the stack and empties the source. Refuses if the destination isn't empty, so items are never duplicated or lost. | the count moved, or 0 if the source was empty |
Examples:
How many items are in slot 0, stored in a score:
execute store result score @s satchel_0 run tma powerinv get @s mypack:satchel container.0Hand one player's slot 0 to another player's matching slot:
tma powerinv move power @s mypack:satchel container.0 power @p[tag=partner] mypack:satchel container.0Pull a power slot into the hotbar, or put the held item into the power:
tma powerinv move power @s mypack:satchel container.0 entity @s hotbar.0
tma powerinv move entity @s weapon.mainhand power @s mypack:satchel container.4Reading item types
Three ways to find out what is in a slot, not just how many:
| From | Use |
|---|---|
Variable code, conditions, ${…} | Item.id(power, slot) and Item.count(power, slot) |
| Power JSON, as a list you can loop over | too-many-attributes:item_list |
| Commands and functions | tma powerinv id |
Item.id and Item.count
held = Item.id("mypack:satchel", 0); // "minecraft:spruce_log", or "minecraft:air" when empty
stack = Item.count("mypack:satchel", 0); // 5
weapon = Item.id("weapon.mainhand"); // a vanilla slot
theirs = Item.id(other, "mypack:satchel", 0); // the target's satchel, in bientity code
gotLogs = Item.id("mypack:satchel", 0).endsWith("_log");| Call | Returns | |
|---|---|---|
Item.id(power, slot) | String | the item in a power inventory slot: its full id, namespace:item. An empty slot is minecraft:air. |
Item.count(power, slot) | int | the stack size there, 0 when empty |
Item.id(slot) | String | the item in a vanilla slot, named as in /item |
Item.count(slot) | int | the stack size there |
- Power slots: the power is its id as a String, and the slot is a plain number, so
Item.id("mypack:satchel", 3)iscontainer.3. - Vanilla slots:
weapon.mainhand,weapon.offhand,armor.head,armor.chest,armor.legs,armor.feet,hotbar.0–hotbar.8,inventory.0–inventory.26,enderchest.0–enderchest.26. The short formsmainhand,offhand,head,chest,legsandfeetwork too. Hands and armor work on any living entity; the rest are players only. - Whose inventory: by default the power holder, or the actor in bi-entity code. Put
selforotherfirst to choose:Item.count(other, "weapon.mainhand").otheronly exists in bi-entity actions and conditions, as elsewhere in the language (page 8). - They're read-only, so they work in
executecode,expressionconditions,modify_variablevalues and${…}(page 9). They don't work in field initializers, which run when the pack loads. - An unknown power, a power the entity doesn't have, a power that isn't an
origins:inventory, an unknown slot name, or a slot outside the inventory is an error: the action stops there and a condition counts asfalse. The message says which.
Example: in a bi-entity action, take note of what the entity you hit is holding.
{
"type": "origins:action_on_hit",
"bientity_action": {
"type": "too-many-attributes:execute",
"code": "lastVictimWeapon = Item.id(other, \"weapon.mainhand\");"
}
}Example: a satchel that only accepts logs, checked when you try to use it.
{
"type": "too-many-attributes:expression",
"expression": "Item.id(\"mypack:satchel\", 0).endsWith(\"_log\") || Item.count(\"mypack:satchel\", 0) == 0"
}Item.cooldown
pearl = Item.cooldown("minecraft:ender_pearl"); // 20 right after throwing one, 0 when it's ready
held = Item.cooldown("weapon.mainhand"); // the item in your main hand
theirs = Item.cooldown(other, "weapon.offhand"); // the target's offhand, in bientity code| Call | Returns | |
|---|---|---|
Item.cooldown(itemOrSlot) | int | ticks left on the item's vanilla cooldown (the one ender pearls use), 0 when it has none |
- The argument is an item id (
minecraft:ender_pearl) or a vanilla slot name, the same names as inItem.id. A slot name reads the item in that slot. - Only players have item cooldowns. For any other entity it's
0. - An unknown item id or slot name is an error, like the calls above.
- The same value as the
item_on_cooldowncondition. Set it withset_item_cooldown.
tma powerinv id
tma powerinv id @s mypack:satchel container.0prints exactly:
minecraft:spruce_logIts result is the item's number in the item registry, so a score can follow a slot's item without any text handling:
execute store result score @s slot0_item run tma powerinv id @s mypack:satchel container.0That number identifies the item type: it's the same for every spruce log and different for stone. It isn't stable across mod or version changes, so compare it with another reading from the same session rather than writing fixed numbers into a pack. Code can read the score back with Scoreboard.get.
Watching a slot for changes
Item.id plus Item.count is a complete fingerprint of a slot: it catches a stack growing or shrinking and a same-size swap (5 logs for 5 stone), which a count alone misses. Keep last tick's fingerprints in a list and compare:
now = Item.id("mypack:satchel", index) + " x" + Item.count("mypack:satchel", index);
changed = !value.equals(now);A complete power that keeps several players' satchels in step is in the cookbook.
Two players sharing items
move makes a duplication-free outbox possible: one player puts an item in their own power slot, and a function moves it into the partner's matching slot. If the partner's slot is full, the item stays where it is.
A shared inventory where both players see the same items isn't safe with commands alone. Mirroring with copy in both directions can duplicate items, because a command can't tell "they took it out" apart from "it was never there". Doing that properly would mean two powers using the same storage, which is a possible future TMA feature.
Tip: ← and → turn pages.
Page 15 of 17
Commands
Every TMA command needs permission level 2, the level of /give and /item. Operators, functions and command blocks have it.
| Command | What it does |
|---|---|
/tma with <targets> <command> | runs a command with ${…} filled in from each target's variables |
/tma powerinv … | reads and writes origins:inventory power slots |
/tma phantom … | spawns, lists and removes phantoms only chosen players can see |
/tma disguise …, /tma undisguise … | makes entities look like another entity type |
/tma global … | looks at and changes the global store |
/tma fakeblock … | shows chosen players a different block at a position |
/tma powertag … | shows which powers a tag covers |
/disableshield <targets> <ticks> | knocks down a raised shield and puts it on cooldown |
/tmavc … | voice chat: broadcast, forced mute, forced deafen |
TMA's attributes are read with vanilla's /attribute.
/tma with
tma with <targets> <command>For each target, fills in every ${expression} in <command> from that target's variables and lists, then runs the command as that target, like execute as. Position, dimension and permission level stay those of whoever ran it.
| Argument | |
|---|---|
<targets> | any entity selector; each target runs the command once |
<command> | the rest of the line: any command, without the leading / |
- Result: the sum of the commands' results.
- Fails, and runs nothing, if any target's placeholders can't be filled in. The error says which target and why.
- Placeholders always become text.
$${is a literal${.
tma with @a title @s actionbar {"text":"${coins} coins"}
tma with @s scoreboard players set @s level ${(int) (xp / 100)}Full details are on page 9.
/tma powerinv
tma powerinv get <target> <power> <slot>
tma powerinv id <target> <power> <slot>
tma powerinv set <target> <power> <slot> <item> [count]
tma powerinv clear <target> <power> [slot]
tma powerinv copy <from> <to>
tma powerinv move <from> <to><from>/<to> are power <target> <power> <slot> or entity <target> <slot>. Slots in a power are container.0, container.1, and so on.
| Subcommand | Result |
|---|---|
get | the item count in the slot |
id | prints the item id as plain text (minecraft:spruce_log); returns its registry number |
set | the count set |
clear | items removed from one slot, or stacks removed from the whole power |
copy | the count copied (overwrites the destination) |
move | the count moved (refuses a non-empty destination) |
Full details and examples are on page 14.
/tma phantom
tma phantom spawn <owner> <entity_type> <pos> <lifetime> [hostile|neutral|passive|stationary] [<viewers>] [<id>]
tma phantom skin <owner> <username> <pos> <lifetime> [hostile|neutral|passive|stationary] [<viewers>] [<id>]
tma phantom remove <id>
tma phantom run <id> <command>
tma phantom remove-all <owner>
tma phantom listSpawns and removes phantoms, the same as the spawn_phantom and despawn_phantom actions. skin spawns a fake player wearing that username's skin.
| Argument | |
|---|---|
<owner> | one entity: the phantom's spawner. It spawns in this entity's dimension, facing the way it faces. |
<entity_type> | what it looks like, like minecraft:zombie; any summonable type |
<username> | (skin) the account whose skin the fake player wears |
<pos> | where it spawns; ~ ~ ~ is where the command runs |
<lifetime> | ticks until it disappears, at least 1 |
hostile/neutral/passive/stationary | its behavior; default neutral for spawn and stationary for skin |
<viewers> | players who can see it. Left out: every player nearby. |
<id> | a whole number, 1 or more, to remove it by later. Left out: the next free id is assigned. Unique across the server: reusing a live id replaces that phantom. |
| Subcommand | Result |
|---|---|
remove <id> | 1 if there was a phantom with that id, otherwise 0. Suggests the ids of live phantoms. |
run <id> <command> | runs <command> as the phantom, at its position, facing and dimension, so @s is the phantom. Returns the command's result; fails if there's no such phantom. See what works on a phantom. |
remove-all <owner> | how many of that entity's phantoms went |
list | every live phantom with its id, type, position, ticks left and viewer count; returns how many |
<viewers>is decided once, when the command runs, likeviewer_conditionwithrecheck: false. Players who join later can't see it.- Unlike the action, a phantom from a command with
<viewers>still depends on its owner: once the owner has left or unloaded, no new viewers get it. - Result:
spawnandskinreturn the phantom's id, and their feedback says it (Spawned phantom 3: …). Store it withexecute store resultto remove that phantom later. They fail for types that can't be summoned. - Assigned ids count up from 1, skipping any that are alive, and start over when the server restarts. A pack that picks its own ids can land on one that was assigned, which replaces that phantom.
- A bare number after
<lifetime>or a behavior is read as<id>, not as a player named with digits. Use a selector for those players. - For a fake player use
skin, notspawn minecraft:player: players aren't a summonable type, sospawnrefuses them.
execute store result score @s stalker run tma phantom spawn @s minecraft:zombie ^ ^ ^8 200 hostile @s
tma phantom skin @s Notch ~ ~ ~2 400 @s 50
tma phantom run 50 effect give @s minecraft:glowing 5
tma phantom remove 50
tma phantom listThe first spawns a zombie 8 blocks in front of you that only you can see and that hunts you for 10 seconds, and keeps its id in your stalker score. The second picks id 50 itself.
To remove a phantom by the id in a score, fill it in with /tma with:
tma with @s tma phantom remove ${Scoreboard.get("stalker")}/tma disguise
tma disguise <targets> <entity_type> [<duration>] [<viewers>]
tma undisguise <targets>Disguises entities as another entity type, or removes the disguise, the same as the disguise and undisguise actions.
| Argument | |
|---|---|
<targets> | entities to disguise or undisguise |
<entity_type> | what they look like; any summonable type except players |
<duration> | ticks until it ends; 0 (the default) lasts until undisguise |
<viewers> | players who see the disguise; everyone else sees the real entity. Left out: everyone. Needs <duration> before it, so write 0 for no limit. |
<viewers>is decided once, when the command runs, likeviewer_conditionwithrecheck: false.- Disguising an entity that's already disguised replaces the disguise.
- Result:
disguisereturns the number of targets;undisguisereturns how many were disguised.disguisefails for players and types that can't be summoned.
tma disguise @s minecraft:creeper 600
tma disguise @e[type=pig,distance=..10] minecraft:ghast 0 @a[team=red]
tma undisguise @a/tma global
tma global get <key>
tma global set <key> <value>
tma global delete <key>
tma global list [<prefix>]
tma global prune <prefix>
tma global prune --older-than <ticks>
tma global lock <key> <owner>
tma global unlock <key>Looks at and changes the global store. Commands ignore locks, so an operator can always fix things. A key or prefix without a namespace gets too-many-attributes:.
| Subcommand | Result |
|---|---|
get | prints the value; returns it if it's a whole number, 1/0 for a boolean, the size of a list, otherwise 1 |
set | sets or creates the key from a Java literal (5, 2.5, true, "text"); returns the new value if it's a number, otherwise 1 |
delete | removes the key; fails if it doesn't exist |
list | prints every key (or those starting with <prefix>) with type, value, age, lock owner and bound entities; returns how many |
prune <prefix> | deletes every key starting with the prefix; returns how many |
prune --older-than <ticks> | deletes every key not written for that many ticks; returns how many |
lock / unlock | locks the key to an owner (a namespace or power id), or removes the lock |
tma global set mypack:gate_open true
tma global list mypack:bond/
execute store result score #kills stats run tma global get mypack:zombie_killsFull details are on page 12.
/tma fakeblock
tma fakeblock set <pos> <block> <viewers> [<duration>]
tma fakeblock clear <pos>Shows the chosen players a different block at <pos>, without changing the world. See fake blocks for what players can and can't do with one.
| Argument | |
|---|---|
<pos> | the block position; its chunk must be loaded |
<block> | what those players see, like minecraft:stone or minecraft:oak_stairs[facing=north] |
<viewers> | the players who see it. Fixed when the command runs, so they still see it after relogging. |
<duration> | ticks until it goes away; left out or 0 lasts until it's cleared or the real block changes |
- Result:
setreturns how many players it was shown to;clearreturns 1 if there was a fake block there, otherwise 0. - Setting one where a fake block already is replaces it.
- Fake blocks aren't saved and end when the server restarts.
tma fakeblock set ~ ~ ~2 minecraft:bedrock @a[team=red] 400
tma fakeblock clear 100 64 -20/tma powertag
tma powertag list
tma powertag list <tag>
tma powertag of <power>Tag names here are always the full name (mypack:magic), since a command has no pack of its own. Shows what the power tag files resolved to after loading: every tag with how many powers it covers, the powers in one tag, or the tags on one power. Returns the number printed.
tma powertag list mypack:magic
tma powertag of origins:phantomize/disableshield
disableshield <targets> <ticks>For each target player who is blocking with a shield right now: lowers the shield, puts shields on cooldown for <ticks>, and plays the shield-break effect. Players who aren't blocking are left alone, so it works as the effect of an ability like a shield bash rather than a free debuff.
| Argument | |
|---|---|
<targets> | players |
<ticks> | cooldown length, at least 1 |
- Result: how many targets were affected.
Example: a shield-breaking hit, from a power:
{
"type": "origins:action_on_hit",
"bientity_action": {
"type": "origins:target_action",
"action": { "type": "origins:execute_command", "command": "disableshield @s 100" }
}
}When you hit a player who is blocking, their shield goes down and can't be raised for 5 seconds. (Suspected: action_on_hit may not run for a hit the shield fully blocked. If so, trigger it from an ability instead, for example an active_self with a raycast.)
/tmavc
Voice chat controls that don't need any power. They need Simple Voice Chat to have an effect.
tmavc broadcast start [<listeners>]
tmavc broadcast stop
tmavc mute start <targets>
tmavc mute stop <targets>
tmavc deafen start <targets>
tmavc deafen stop <targets>| Subcommand | What it does |
|---|---|
broadcast start | You (the player running the command) broadcast your voice to every online player, or only to <listeners> if given. It lasts until broadcast stop. |
broadcast stop | Ends your broadcast |
mute start / stop | Force-mutes the targets: their microphone audio reaches nobody. Separate from the player's own mic toggle. |
deafen start / stop | Force-deafens the targets: they hear no voice audio at all. |
broadcastmust be run by a player.- Result:
broadcastreturns 1;muteanddeafenreturn the number of targets. - None of this is saved: it all ends when the server restarts.
Details, and the matching power actions and conditions, are on page 13.
Tip: ← and → turn pages.
Page 16 of 17
Power tags
A power tag is a name for a group of powers, listed in a datapack file. Powers don't have to know about it: you tag them from outside, including powers from Origins itself or another pack.
That turns "which powers does this apply to?" into a list you keep in one place. Instead of every power checking a shared silenced variable itself (so a new power only behaves if its author remembered), you tag the handful of powers that are the exception and check the tag.
The file
Each pack has one file: data/<namespace>/tma_power_tags.json. It maps a tag name to the powers in it.
{
"movement": [
"mypack:double_jump",
"mypack:wall_climb",
"gravity-shifter:*"
],
"otherpack:unsilenceable": [
"mypack:heartbeat"
]
}*matches any run of characters, sogravity-shifter:*is every power in that pack, andmypack:fire_*is every power whose id starts that way.- A tag name without a namespace gets the pack's own. In
data/mypack/tma_power_tags.json,"movement"is the tagmypack:movement, so two packs can each use short names without colliding. Write the full name, like"otherpack:unsilenceable"above, to add powers to another pack's tag. - Files from every pack are merged. If two packs both add to
otherpack:unsilenceable, the tag holds both lists. A tag can also name powers from a pack that isn't installed; those entries simply match nothing. - Nothing is validated against the power list. A typo isn't an error, it just matches no powers. Check with
/tma powertag. - The file is reloaded with
/reload, like any other datapack file.
Tags cover powers, not origins. To group origins, tag a power each of those origins grants.
Has power tag
too-many-attributes:has_power_tagentity condition and bi-entity condition
True when the entity holds a power in that tag.
| Field | Type | Default | Description |
|---|---|---|---|
tag | String | required | The tag name. Without a namespace it gets this power's own namespace, the same way the tag file does, so a power in mypack asking for "movement" means mypack:movement. |
include_inactive | Boolean | false | By default only active powers count (their own condition is true). true counts every power the entity has. |
- Hidden powers count, including the sub-powers of
origins:multiple. That's usually what you want: an origin's ability is often a hidden sub-power. - The bi-entity version tests the target, so a power can react to what the other entity is.
- It works for any entity that can have powers, not just players.
Example: an anti-magic aura. Hits from anyone holding a power tagged mypack:magic hurt you less.
{
"type": "origins:modify_damage_taken",
"bientity_condition": {
"type": "too-many-attributes:has_power_tag",
"tag": "mypack:magic"
},
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.5 }
}Example: a power that only does something for players who don't have a flight ability.
{
"type": "origins:conditioned_attribute",
"condition": {
"type": "too-many-attributes:has_power_tag",
"tag": "mypack:flight",
"inverted": true
},
"modifier": { "attribute": "minecraft:generic.movement_speed", "operation": "multiply_base", "value": 0.15 }
}Listing tagged powers
The power_list power takes a tag field, so a list holds only the holder's powers in that tag:
{
"type": "too-many-attributes:power_list",
"variable": "my_magic",
"tag": "mypack:magic",
"only_active": true
}From there, my_magic.size(), for_each and the other list tools work as usual (page 11).
/tma powertag
tma powertag list
tma powertag list <tag>
tma powertag of <power>Shows what the tag files resolved to after loading, which is the quickest way to catch a typo or a wildcard that matched more than you meant.
| Subcommand | What it prints | Result |
|---|---|---|
list | every tag and how many loaded powers it covers | the number of tags |
list <tag> | the powers in that tag, and the entries it was written with | how many powers |
of <power> | every tag that power is in | how many tags |
tma powertag list mypack:magic
tma powertag of origins:phantomizeTip: ← and → turn pages.
Page 17 of 17
Cookbook
Complete powers that combine several TMA features. Each recipe lists its files. Put them in data/mypack/powers/ and add the powers to an origin.
(These recipes are put together from the documented behavior of each part. They haven't been tested in-game as whole recipes.)
| Recipe | Uses |
|---|---|
| Rage | variables, variable_bar, recent_damage_taken, modifier resource |
| Combo counter | variables, bi-entity execute, expression, with_variables |
| Team bank | variables, Scoreboard.get, with_variables, /tma with |
| Loud footsteps | is_moving_loudly, vibration_on_cooldown, set_vibration_cooldown |
| Radio | start_broadcast_voice, stop_broadcast_voice, is_broadcasting_voice |
| Linked inventory | Item.id, Item.count, list, for_each, for_each_read, with_variables, /tma powerinv copy |
| Bond | Global.set, Global.bind, Global.has, bi-entity expression, variables |
Rage
Taking damage builds rage (up to 100). Rage fades when you're not being hit, and every 20 rage adds 1 damage to your attacks.
rage.json:
{
"type": "too-many-attributes:variables",
"fields": [
"int rage = 0;",
"int rageDamage = 0; // rage / 20, for the damage modifier"
]
}rage_bar.json: the visible bar.
{
"type": "too-many-attributes:variable_bar",
"variable": "rage",
"min": 0,
"max": 100,
"hud_render": { "should_render": true, "bar_index": 2 }
}rage_damage_bar.json: a hidden bar, only so the modifier can read rageDamage.
{
"type": "too-many-attributes:variable_bar",
"variable": "rageDamage",
"min": 0,
"max": 5,
"hud_render": { "should_render": false }
}rage_tick.json: gain rage while hurt, lose it otherwise.
{
"type": "origins:action_over_time",
"interval": 20,
"entity_action": {
"type": "origins:if_else",
"condition": {
"type": "too-many-attributes:recent_damage_taken",
"window_ticks": 20,
"comparison": ">",
"compare_to": 0
},
"if_action": {
"type": "too-many-attributes:execute",
"code": ["rage = Math.min(rage + 10, 100);", "rageDamage = rage / 20;"]
},
"else_action": {
"type": "too-many-attributes:execute",
"code": ["rage = Math.max(rage - 5, 0);", "rageDamage = rage / 20;"]
}
}
}rage_damage.json: the bonus damage.
{
"type": "origins:modify_damage_dealt",
"modifier": { "operation": "add_base_early", "value": 0, "resource": "mypack:rage_damage_bar" }
}How it fits together:
recent_damage_takenwith a 20-tick window is "was I hurt in the last second".- The code keeps
rageDamagein step withrage, using whole-number division. - The modifier reads
rageDamagethrough the hidden bar (page 9).
Combo counter
Each hit within 2 seconds of the last one adds to a combo. At 5 or more, your hits deal 25% more. The action bar shows the count.
combo.json:
{
"type": "too-many-attributes:variables",
"fields": ["int combo = 0;", "int comboTimer = 0;"]
}combo_hit.json: count hits.
{
"type": "origins:action_on_hit",
"bientity_action": { "type": "too-many-attributes:execute", "code": "combo++; comboTimer = 40;" }
}combo_tick.json: the timer, and the action bar.
{
"type": "origins:action_over_time",
"interval": 1,
"entity_action": {
"type": "origins:and",
"actions": [
{
"type": "too-many-attributes:execute",
"code": ["comboTimer = Math.max(comboTimer - 1, 0);", "combo = comboTimer == 0 ? 0 : combo;"]
},
{
"type": "origins:if_else",
"condition": { "type": "too-many-attributes:expression", "expression": "combo > 0" },
"if_action": {
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:execute_command",
"command": "title @s actionbar {\"text\":\"Combo x${combo}\",\"color\":\"${combo >= 5 ? \"gold\" : \"white\"}\"}"
}
}
}
]
}
}combo_bonus.json:
{
"type": "origins:modify_damage_dealt",
"condition": { "type": "too-many-attributes:expression", "expression": "combo >= 5" },
"modifier": { "operation": "multiply_total_multiplicative", "value": 0.25 }
}combo_hit.json is a bi-entity action, where a bare combo means the actor's, which is you (page 8). The target doesn't need the variables.
Team bank
Everyone with the power has their own coins, and can deposit into one shared bank kept on a scoreboard.
load.mcfunction (run on load):
scoreboard objectives add team_bank dummywallet.json:
{
"type": "too-many-attributes:variables",
"fields": ["long coins = 100;"]
}deposit.json: press your primary key to deposit up to 10 coins.
{
"type": "origins:active_self",
"key": { "key": "key.origins.primary_active" },
"condition": { "type": "too-many-attributes:expression", "expression": "coins > 0" },
"entity_action": {
"type": "origins:and",
"actions": [
{
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:execute_command",
"command": "scoreboard players add #bank team_bank ${Math.min(coins, 10)}"
}
},
{ "type": "too-many-attributes:execute", "code": "coins -= Math.min(coins, 10);" },
{
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:execute_command",
"command": "title @s actionbar {\"text\":\"You: ${coins} Bank: ${Scoreboard.get(\"#bank\", \"team_bank\")}\"}"
}
}
]
}
}A function can show everyone their wallet and the bank:
tma with @a[scores={team_bank=0..}] title @s actionbar {"text":"Bank: ${Scoreboard.has("#bank", "team_bank") ? Scoreboard.get("#bank", "team_bank") : 0}"}
tma with @a title @s actionbar {"text":"Wallet: ${coins}"}The second /tma with fails as a whole if any player in @a lacks the coins variable. Narrow the selector (for example with a tag) when not everyone has the power.
Loud footsteps
Your running makes a sculk click that everyone nearby hears, at most every 2 seconds. An ability silences you for 10 seconds.
footsteps.json:
{
"type": "origins:action_over_time",
"interval": 1,
"condition": { "type": "too-many-attributes:vibration_on_cooldown" },
"rising_action": {
"type": "origins:execute_command",
"command": "playsound minecraft:block.sculk_sensor.clicking player @a ~ ~ ~ 1 1"
}
}hush.json:
{
"type": "origins:active_self",
"key": { "key": "key.origins.secondary_active" },
"cooldown": 600,
"hud_render": { "should_render": true },
"entity_action": { "type": "too-many-attributes:set_vibration_cooldown", "ticks": 200 }
}TMA starts a 40-tick cooldown each time you move loudly while it's at 0, so rising_action fires once per "vibration". Hush holds the cooldown up for 200 ticks, so no new vibration starts (page 6).
Radio
Hold a goat horn to talk to everyone else who has the radio power, anywhere on the server. Needs Simple Voice Chat.
radio.json:
{
"type": "origins:action_over_time",
"interval": 5,
"condition": {
"type": "origins:equipped_item",
"equipment_slot": "mainhand",
"item_condition": { "type": "origins:ingredient", "ingredient": { "item": "minecraft:goat_horn" } }
},
"rising_action": {
"type": "too-many-attributes:start_broadcast_voice",
"listeners": {
"type": "origins:and",
"conditions": [
{ "type": "origins:target_condition", "condition": { "type": "origins:power", "power": "mypack:radio" } },
{ "type": "too-many-attributes:in_voice_range_of", "inverted": true }
]
}
},
"falling_action": { "type": "too-many-attributes:stop_broadcast_voice" }
}radio_light.json: a spark above you while you're on the air.
{
"type": "origins:action_over_time",
"interval": 10,
"condition": { "type": "too-many-attributes:is_broadcasting_voice" },
"entity_action": {
"type": "origins:execute_command",
"command": "particle minecraft:electric_spark ~ ~2.2 ~ 0.2 0.2 0.2 0 4"
}
}The broadcast starts when you take the horn in hand and stops when you put it away. Players within normal voice range are left out, because they already hear you (page 13).
Linked inventory
Everyone with the power has their own 9-slot satchel, and the satchels are kept the same. Put something in slot 4 and it appears in everyone else's slot 4; take it out and it disappears from theirs.
There's no shared storage anywhere. Each player watches their own slots, and when one changes, that player pushes the real stack to everyone else.
linked_inv.json: the satchel itself.
{
"type": "origins:inventory",
"container_type": "dropper",
"title": "Linked satchel",
"key": { "key": "key.origins.primary_active" }
}snapshot.json: what each slot looked like last tick, one element per slot.
{
"type": "too-many-attributes:list",
"hidden": true,
"variable": "snapshot",
"element_type": "String",
"max_size": 9,
"initial": [
"minecraft:air x0", "minecraft:air x0", "minecraft:air x0",
"minecraft:air x0", "minecraft:air x0", "minecraft:air x0",
"minecraft:air x0", "minecraft:air x0", "minecraft:air x0"
]
}sync.json: every tick, push changed slots, then write down what each slot holds now.
{
"type": "origins:action_over_time",
"interval": 1,
"entity_action": {
"type": "origins:and",
"actions": [
{ "type": "origins:execute_command", "command": "tag @s add linked" },
{
"type": "too-many-attributes:for_each_read",
"list": "snapshot",
"action": {
"type": "origins:if_else",
"condition": {
"type": "too-many-attributes:expression",
"expression": "!value.equals(Item.id(\"mypack:linked_inv\", index) + \" x\" + Item.count(\"mypack:linked_inv\", index))"
},
"if_action": {
"type": "origins:and",
"actions": [
{ "type": "origins:execute_command", "command": "tag @s add linked_source" },
{
"type": "too-many-attributes:with_variables",
"action": {
"type": "origins:execute_command",
"command": "execute as @a[tag=linked,tag=!linked_source] run tma powerinv copy power @a[tag=linked_source,limit=1] mypack:linked_inv container.${index} power @s mypack:linked_inv container.${index}"
}
},
{ "type": "origins:execute_command", "command": "tag @s remove linked_source" }
]
}
}
},
{
"type": "too-many-attributes:for_each",
"list": "snapshot",
"code": "value = Item.id(\"mypack:linked_inv\", index) + \" x\" + Item.count(\"mypack:linked_inv\", index);"
}
]
}
}How it works:
- The fingerprint is
Item.id(...) + " x" + Item.count(...), e.g.minecraft:spruce_log x5, andminecraft:air x0for an empty slot. Because it includes the id, swapping 5 logs for 5 stone is a change, which a count on its own would miss. for_each_readwalks the snapshot and compares each slot with the fingerprint, usingindexas the slot number.${index}puts that slot number into the copy command, which moves the real stack, whatever it is.- The tag dance (
linked_source) is how the copy command names "the player whose slot changed" while running as each other player. for_eachthen writes the current fingerprints back into the snapshot, so each change pushes once.
Worth knowing before you build on it:
copyoverwrites. If two players change the same slot in the same tick, one push wins and the other's item is overwritten and gone. For a safe version, only push into slots the other player has empty, and check withItem.id.- A player who joins keeps their own satchel until someone changes a slot; from then on they follow along.
- It runs 9 checks per player per tick. That's cheap, but if you make the satchel much bigger, raise
interval.
(This recipe hasn't been run in-game. The pieces it uses are each documented, but the whole has only been checked by the parser.)
Bond
Two players bond by sneaking and right-clicking each other. Bonded players take 20% less damage from each other's hits. A bond ends by itself once both players have been offline for good (the tmaPlayerExpiryDays gamerule).
Code can't read a player's name or UUID, so each player first gets a number from a global counter, and a bond's key is made from the two numbers.
bond_id.json: this player's number, handed out once.
{
"type": "too-many-attributes:variables",
"hidden": true,
"fields": "int bond_id = 0;"
}assign_id.json: the first tick a player has the power, take the next number.
{
"type": "origins:action_over_time",
"interval": 20,
"condition": { "type": "too-many-attributes:expression", "expression": "bond_id == 0" },
"entity_action": {
"type": "too-many-attributes:execute",
"code": [
"Global.set(\"mypack:bond_counter\", Global.has(\"mypack:bond_counter\") ? Global.get(\"mypack:bond_counter\") + 1 : 1);",
"bond_id = Global.get(\"mypack:bond_counter\");"
]
}
}bond.json: sneak and right-click another player to bond.
{
"type": "origins:action_on_entity_use",
"condition": { "type": "origins:sneaking" },
"bientity_condition": {
"type": "too-many-attributes:expression",
"expression": "self.bond_id > 0 && other.bond_id > 0"
},
"bientity_action": {
"type": "too-many-attributes:execute",
"code": [
"Global.set(\"mypack:bond/\" + Math.min(self.bond_id, other.bond_id) + \"/\" + Math.max(self.bond_id, other.bond_id), true);",
"Global.bind(\"mypack:bond/\" + Math.min(self.bond_id, other.bond_id) + \"/\" + Math.max(self.bond_id, other.bond_id), self);",
"Global.bind(\"mypack:bond/\" + Math.min(self.bond_id, other.bond_id) + \"/\" + Math.max(self.bond_id, other.bond_id), other);"
]
},
"cooldown": 20
}bonded_mercy.json: hits from a bonded player hurt less.
{
"type": "origins:modify_damage_taken",
"bientity_condition": {
"type": "too-many-attributes:expression",
"expression": "Global.has(\"mypack:bond/\" + Math.min(self.bond_id, other.bond_id) + \"/\" + Math.max(self.bond_id, other.bond_id))"
},
"modifier": { "operation": "multiply_total_multiplicative", "value": -0.2 }
}How it works:
- The counter is one global integer every player's power reads and writes, so numbers never repeat.
- The key puts the smaller number first, so A-with-B and B-with-A are the same bond.
- Binding both players means the key is deleted only after both are gone, not when one of them leaves.
- The other player needs the powers too:
other.bond_idmust exist, and the expression fails (counts as false) for anyone withoutbond_id.json. - In
modify_damage_taken, the actor is the attacker and the target is you (check the order in-game). The key doesn't care, since it uses min and max.
(This recipe hasn't been run in-game.)
Tip: ← and → turn pages.