Too Many Attributes

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, the fake_blocks power, /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 /attribute and Origins' origins:attribute condition, with no power needed.
  • A settable drag attribute 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_downed condition for players knocked out in Hardcore Revival.
  • Commands: /tma with (variables in any command), /tma powerinv (read and write origins:inventory powers), /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

IdKind
recent_damage_blockedentity condition, attributedamage blocked with a shield in the last window_ticks ticks (replaces shield_block_dot)
phantom_actionentity actionruns actions on your phantoms, or one by id
phantomentity conditioncounts your phantoms, or checks one by id
action_on_phantom_hitpower typeruns 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 spawn and skin return the id and say it in their feedback, so execute store result can 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, neutral and passive now work for them too, with the normal walk animation. Left out, a fake player is still stationary. /tma phantom skin takes a behavior like spawn does.
  • Phantoms can't be hurt or killed by anything, including /kill, so a phantom mob can't drop real loot. Remove one with despawn_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.

OldNew
shield_block_dot condition and attributeRemoved. 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_phantomA 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:spirittma phantom remove 7001
/tma phantom spawn and skin return 1They return the phantom's id.
  • Fake players from spawn_phantom with a behavior set 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

IdKind
statentitya vanilla statistic (blocks mined, jumps, mobs killed, …)
is_sleepingentityin bed right now
redstone_powerblock, entityredstone signal 0–15, received, strong or emitted
has_power_tagentity, bi-entityholds a power in a power tag
last_hit_was_critentitythe last attack it made was a critical
last_damage_was_critentitythe last damage it took was a critical

TMA now has block conditions at all, on the new page 5.

New actions

IdKind
set_rotation, look_atentity, bi-entityturn an entity's view, instantly or a step at a time
play_block_crackblockthe mining-crack overlay, with no real mining
spawn_phantomentitya mob only chosen players can see
despawn_phantomentityremoves a phantom by id, or all of one entity's
disguise, undisguiseentitymake an entity look like another type, per viewer
set_fake_block, clear_fake_blockentity, blockshow chosen players a different block
emit_signalentityannounce something other powers can react to
modify_global, delete_globalentity, bi-entitychange the global store from JSON

New power types

Id
action_on_sleep_startruns when the holder falls asleep
action_on_chatruns when a chat message contains your text, sent or received
action_on_signalruns when a matching signal is emitted
fake_blocksblocks that look different near the holder
global_variablesdeclared 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 id on spawn_phantom, unique across the server, plus despawn_phantom and /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) and Global.*.
  • New attributes: last_death_distance and redstone_power.
  • New commands: /tma phantom, /tma disguise, /tma global, /tma fakeblock and /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:…)KindUse instead
fall_distanceentity conditionorigins:fall_distance
oxygen_remainingentity conditionorigins:air
is_glidingentity conditionorigins:fall_flying
potion_amplifierentity conditionorigins:status_effect with min_amplifier / max_amplifier
sky_visibleentity conditionorigins:exposed_to_sky
wall_collisionentity conditionorigins:collided_horizontally
weather_intensityentity conditionorigins:in_rain
is_targetingbi-entity conditionorigins:attack_target
durability_percentitem conditionorigins:relative_durability
  • Attributes removed with them: fall_distance, oxygen_remaining, is_gliding, sky_visible, wall_collision, rain_level and thunder_level. In functions, data get entity @s FallDistance and data get entity @s Air read the same vanilla values.
  • potion_amplifier checks with a comparison become a range. For example, ">=", 1 becomes "min_amplifier": 1. For "doesn't have the effect", use origins:status_effect with "inverted": true.
  • New: friction, a condition and attribute for the effective slipperiness of the block you move on, including origins:modify_slipperiness powers.
  • New: is_downed, a condition and attribute for whether a player is knocked out in Hardcore Revival - optional, false without 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_target now works for goal-based mobs (zombies, skeletons, …), not just brain-based ones (piglins, wardens, …).

Pages

#PageWhat's in it
1Getting startedRequirements, installing, your first TMA power, how these docs are laid out
2Entity conditionsThe 40 condition types: combat, movement, body, world, mobs, items, integrations
3Bi-entity conditionsis_observed_by, has_power_tag
4Damage conditionsdamage_source_nbt
5Block conditionsredstone_power
6Entity actionssignals, 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
7AttributesThe 45 read-only attributes, plus the settable drag
8VariablesDeclaring, changing and checking per-player variables; HUD bars
9Variables in Origins fields, commands and scoreboardswith_variables, /tma with, Scoreboard.get, modifier resource
10Language referenceThe Java expression language: operators, types, built-ins (including Death, Stat and Global), errors
11Lists and loopslist, power_list, for_each, for_each_read
12Global storeValues shared by every entity: Global.*, global_variables, modify_global, locking, cleanup, /tma global
13Voice chatSimple Voice Chat conditions, broadcast, mute and deafen
14Power inventoriesReading and writing origins:inventory powers
15CommandsEvery command in one place
16Power tagsGrouping powers in a datapack file, the has_power_tag condition, /tma powertag
17CookbookComplete powers that combine several features

Every id

Condition types

IdKindPage
attack_cooldown_progressentity2
biome_temperatureentity2
critical_hit_eligibleentity2
crouch_depthentity2
damage_source_nbtdamage4
distance_to_nearest_playerentity2
exhaustionentity2
expressionentity, bi-entity8
frictionentity2
glide_ratioentity2
has_attack_targetentity2
has_power_tagentity, bi-entity16
hurt_timeentity2
in_same_voice_groupbi-entity13
in_voice_groupentity13
in_voice_range_ofbi-entity13
invulnerable_ticksentity2
is_blockingentity2
is_broadcasting_voiceentity13
is_deafened_voiceentity13
is_jumpingentity2
is_moving_loudlyentity2
is_muted_voiceentity13
is_observed_bybi-entity3
is_sleepingentity2
is_talkingentity13
is_whisperingentity13
item_on_cooldownentity2
last_attack_reachentity2
last_damage_amountentity2
last_damage_was_critentity2
last_damage_typeentity2
last_hit_was_critentity2
light_at_eyesentity2
look_angleentity2
moon_phaseentity2
phantomentity6
projectile_ageentity2
recent_damage_takenentity2
recent_damage_blockedentity2
recent_knockbackentity2
redstone_powerblock, entity5, 2
regional_difficultyentity2
sprint_ticksentity2
statentity2
submersion_depthentity2
time_airborneentity2
time_since_last_mealentity2
velocityentity2
vibration_on_cooldownentity2
voice_installedentity13
voice_mic_disabledentity13
with_variablesentity, bi-entity9
xp_progressentity2

Action types

IdKindPage
clear_fake_blockentity, block6
delete_globalentity, bi-entity12
despawn_phantomentity6
disguiseentity6
executeentity, bi-entity8
for_eachentity, bi-entity11
for_each_readentity, bi-entity11
emit_signalentity6
look_atentity, bi-entity6
modify_globalentity, bi-entity12
modify_variableentity, bi-entity8
phantom_actionentity6
play_block_crackblock6
set_fake_blockentity, block6
set_item_cooldownentity6
set_rotationentity6
set_vibration_cooldownentity6
spawn_phantomentity6
start_broadcast_voiceentity13
stop_broadcast_voiceentity13
undisguiseentity6
with_variablesentity, bi-entity9

Power types

IdPage
variables8
variable_bar8
global_variables12
list11
power_list11
item_list11
action_on_death6
action_on_join6
action_on_disconnect6
action_on_start_tracking6
action_on_mob_conversion6
action_on_sleep_start6
action_on_signal6
fake_blocks6
action_on_chat6
action_on_phantom_hit6

Commands

CommandPage
/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
TMA2.2.0
Minecraft1.20.1
Fabric Loader0.19.3 or newer
Fabric APIany
Origins1.10.0 or newerincludes Apoli and Calio, which TMA builds on
Java17 or newer
Simple Voice Chatany, optionalonly needed for the voice chat features (page 13)
Hardcore Revivalany, optionalonly 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.

json
{
  "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:

mcfunction
power grant @s mypack:steady_aim

How these docs are laid out

Each type gets a block like the Origins docs:

Sprint ticks

too-many-attributes:sprint_ticksentity condition

What it checks, in one or two sentences.

FieldTypeDefaultDescription

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 axis or component) 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, default 30, 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_render condition, reads them as 0 or false. The attributes are synced to clients every tick, so client-side checks can use origins:attribute instead.
  • 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:

FieldTypeDefaultDescription
comparisonComparisonrequiredHow to compare the value with compare_to: "<", "<=", ">", ">=", "==" or "!="
compare_toInteger, Float or Double (see each entry)requiredThe 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

GroupConditions
Combatattack_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
Movementvelocity, look_angle, is_jumping, time_airborne, sprint_ticks, crouch_depth, glide_ratio, friction, is_moving_loudly, submersion_depth
Bodyexhaustion, time_since_last_meal, xp_progress, stat, is_sleeping
Worldlight_at_eyes, biome_temperature, moon_phase, regional_difficulty, distance_to_nearest_player, redstone_power
Powershas_power_tag
Mobs and other entitieshas_attack_target, projectile_age
Phantomsphantom, documented with the other phantom types on page 6
Itemsitem_on_cooldown
Vibrationsvibration_on_cooldown
Integrationsis_downed

Removed in 2.1.0 because Origins already has them. Use the Origins type instead:

RemovedUse
fall_distanceorigins:fall_distance
oxygen_remainingorigins:air
is_glidingorigins:fall_flying
potion_amplifierorigins:status_effect with min_amplifier / max_amplifier
sky_visibleorigins:exposed_to_sky
wall_collisionorigins:collided_horizontally
weather_intensityorigins: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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequired0.0 – 1.0

Example:

json
{
  "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_progress to get closer.

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toDoublerequiredblocks
  • It's 0 until 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_dealt it's the distance of the current hit. (From source.)

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequiredticks, 0 – 10
  • False for entities that aren't living (like boats).

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequiredticks

Example:

json
{
  "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.

FieldTypeDefaultDescription
window_ticksInteger60How far back to add up, in ticks. At most 600 (30 seconds); larger values act like 600.
comparisonComparisonrequired
compare_toFloatrequireddamage 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.

json
{
  "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.

FieldTypeDefaultDescription
window_ticksInteger60How far back to add up, in ticks. At most 600 (30 seconds); larger values act like 600.
comparisonComparisonrequired
compare_toFloatrequireddamage 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.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequireddamage points
  • It keeps its value until the next hit, so combine it with hurt_time for "just now".
  • Hits blocked by a shield count here too.

Example: heavy hits stagger you.

json
{
  "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.

FieldTypeDefaultDescription
valueStringrequiredThe 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_attack has the message id player. 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.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toDoublerequired
  • It's the strength vanilla asked for, before knockback resistance reduces it. (From source.)
  • It keeps its value until the next knockback.

Example:

json
{
  "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_dealt or origins:action_on_hit for that hit, it still holds the previous attack's answer. To react to a crit, run the check a tick later (an action_over_time with interval: 1 and a rising_action), or use critical_hit_eligible, which asks whether a hit would crit right now.
  • It's false until the entity's first attack, isn't saved, and resets when the server restarts.

Example: after landing a critical hit, you glow briefly.

json
{
  "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 false until the entity is first damaged, and isn't saved.

Example: being critted makes you flee-footed for a moment.

json
{
  "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:

json
{
  "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.

FieldTypeDefaultDescription
axisStringrequiredx, y or z. y is up (negative while falling).
comparisonComparisonrequired
compare_toDoublerequiredblocks per tick (1 block/tick = 20 blocks/second)

Example: feather fall.

json
{
  "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.

FieldTypeDefaultDescription
componentStringrequiredpitch (up/down) or yaw (left/right)
comparisonComparisonrequired
compare_toFloatrequireddegrees
  • Pitch: −90 is straight up, 0 level, 90 straight down.
  • Yaw: 0 faces south (+Z), 90 west, ±180 north, −90 east. (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.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequiredticks
  • Anything that isn't "on the ground" counts: jumping, falling, swimming, flying and gliding.

Example: glider.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequiredticks

Example: second wind.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequired0.0 – 1.0
  • Only players are tracked; it's 0 for other entities.

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequired
  • False when not gliding, whatever the comparison.

Example:

json
{
  "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.

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequiredslipperiness
  • Block values: 0.6 for most blocks, 0.8 for slime blocks, 0.98 for ice, packed ice and frosted ice, and 0.989 for 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 add origins:on_block when that matters.
  • Reading instead of checking: to test a block's own slipperiness without the power modifiers, use Origins' origins:on_block with an origins:slipperiness block condition. This condition, and its attribute, give you the effective value, which Origins can't read.

Example: skates. Faster on anything slippery.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequiredblocks (always whole)

Example: water pressure.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequiredusually 0.0 – 4.0

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequiredticks
  • 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:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequired0.0 – 1.0

Example:

json
{
  "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.

FieldTypeDefaultDescription
statObjectrequiredWhich statistic: { "type": "<stat type>", "id": "<id>" }. Same format as Origins' origins:modify_stat action.
comparisonComparisonrequired
compare_toIntegerrequiredThe 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_render this is always false.
  • To change a stat, use Origins' origins:modify_stat. For maths on stats (per hour, ratios), use Stat.get in code.

Example: "Veteran miner". After mining 10,000 stone, you mine faster.

json
{
  "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_start power.
  • 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.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequired0 – 15

Example: light-sensitive.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequired

Example: heat-adapted.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequired0 – 7

Example: werewolf.

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toFloatrequired

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toDoublerequiredblocks

Example: hermit.

json
{
  "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.

FieldTypeDefaultDescription
tagStringrequiredThe tag name, as written in the file.
include_inactiveBooleanfalseCount powers whose own condition is false too.

A tag name without a namespace gets the namespace of the power the condition is written in.

json
{ "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequired0 – 15
json
{ "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_target bi-entity condition.

Example:

json
{
  "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.

FieldTypeDefaultDescription
comparisonComparisonrequired
compare_toIntegerrequiredticks
  • 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.

json
{
  "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.

FieldTypeDefaultDescription
itemIdentifiernoneThe item to check, like minecraft:ender_pearl. When it's set, slot is ignored.
slotString"weapon.mainhand"Without item: check whatever item is in this slot. Same names as Item.id: weapon.mainhand, weapon.offhand, armor.headarmor.feet, hotbar.08, inventory.026, enderchest.026.
comparisonComparisonnoneOptional. With it, compares the ticks left on the cooldown with compare_to.
compare_toInteger0ticks
  • Without comparison it's true while the item is on cooldown at all.
  • With comparison, an item that isn't on cooldown has 0 ticks 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 slot name 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 with Item.cooldown.

Example: a pearl's recoil. While your ender pearl is cooling down, you're slower.

json
{
  "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.

json
{ "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:

No fields.

For a moving player, it turning true means "a vibration just happened". Origins' rising_action runs on exactly that moment.

Example:

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"
  }
}

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.

json
{
  "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.

IdWhere
too-many-attributes:is_observed_bythis page
too-many-attributes:has_power_tagpage 16: the target holds a power with that tag
too-many-attributes:expressionpage 8: check both entities' variables
too-many-attributes:with_variablespage 9: any bi-entity condition with ${…} values
too-many-attributes:in_same_voice_grouppage 13
too-many-attributes:in_voice_range_ofpage 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.

FieldTypeDefaultDescription
fov_degreesFloat60.0Width 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_distanceFloat32.0The 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_see bi-entity condition in an origins: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.

json
{
  "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.

FieldTypeDefaultDescription
keyStringrequiredA top-level NBT key of that entity
valueStringrequiredThe 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" is 1b. (From source.)
  • Only top-level keys work. There are no paths like Item.id.
  • Entity ids aren't in this data; use Origins' origins:projectile damage condition for the projectile type.

Example: crossbow bolts hurt you less.

json
{
  "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.

FieldTypeDefaultDescription
modeString"received"Which signal to read. See below.
directionStringnoneOnly for mode: emitted: the side the signal leaves through, down, up, north, south, west or east. Left out: the strongest side.
comparisonComparisonrequired
compare_toIntegerrequired0 – 15

The three modes are vanilla's three redstone reads:

modeReadsUse it for
receivedthe strongest signal reaching the block from any side"is this block powered?", the way a lamp, piston or door checks
strongthe strongest strong power going into the blockwhether a solid block is powered hard enough to pass power on to wire next to it
emittedwhat the block itself sends outwire, torches, levers, buttons, pressure plates and powered solid blocks
  • A block with nothing powering it reads 0 in every mode.
  • emitted with a direction is 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.

json
{
  "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.

json
{
  "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.

json
{ "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 received means 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, use origins:on_block with the block condition, as in the first example.
json
{ "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:

IdKindWhere
too-many-attributes:set_vibration_cooldownentitythis page
too-many-attributes:set_item_cooldownentitythis page
too-many-attributes:set_rotationentitythis page
too-many-attributes:look_atentity, bi-entitythis page
too-many-attributes:play_block_crackblockthis page
too-many-attributes:spawn_phantomentitythis page
too-many-attributes:despawn_phantomentitythis page
too-many-attributes:phantom_actionentitythis page
too-many-attributes:disguiseentitythis page
too-many-attributes:undisguiseentitythis page
too-many-attributes:emit_signalentitythis page
too-many-attributes:set_fake_blockentity, blockthis page
too-many-attributes:clear_fake_blockentity, blockthis page
too-many-attributes:executeentity, bi-entitypage 8: run variable code
too-many-attributes:modify_variableentity, bi-entitypage 8: change one variable, as JSON
too-many-attributes:with_variablesentity, bi-entitypage 9: any action with ${…} values
too-many-attributes:for_eachentity, bi-entitypage 11: code for each list element
too-many-attributes:for_each_readentity, bi-entitypage 11: an action for each list element
too-many-attributes:start_broadcast_voiceentitypage 13
too-many-attributes:stop_broadcast_voiceentitypage 13
too-many-attributes:modify_globalentity, bi-entitypage 12: change a global value
too-many-attributes:delete_globalentity, bi-entitypage 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.

FieldTypeDefaultDescription
ticksIntegerrequiredThe 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 0 gets 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, and vibration_on_cooldown turning 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.

json
{
  "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.

FieldTypeDefaultDescription
itemIdentifiernoneThe item, like minecraft:ender_pearl. When it's set, slot is ignored.
slotString"weapon.mainhand"Without item: the item in this slot. Same names as Item.id.
ticksIntegerrequiredThe 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 slot name 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.

json
{
  "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.

json
{ "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.

FieldTypeDefaultDescription
yawFloatnoneLeft/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.
pitchFloatnoneUp/down angle in degrees: -90 straight up, 90 straight down. Left out: pitch isn't changed.
relativeBooleanfalseWhen true, yaw and pitch are added to the current angles instead of replacing them.
max_stepFloatnoneThe 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_stepFloat0A 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_step sends one per call, so it's more packets than one snap. Set min_step (for example 1) 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.

json
{
  "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:

FieldTypeDefaultDescription
x, y, zDoublerequiredThe point to look at.
relative_toString"world"world: the numbers are world coordinates. self: they're an offset from the entity's feet, like ~ ~ ~.
max_stepFloatnoneAs in set_rotation.
min_stepFloat0As in set_rotation.

As a bi-entity action, the actor looks at the target:

FieldTypeDefaultDescription
anchorString"eyes"Which part of the target to look at: eyes, feet or center (the middle of its hitbox).
max_stepFloatnoneAs in set_rotation.
min_stepFloat0As in set_rotation.

Example: "Stare down". Hitting a mob turns your view to its eyes.

json
{
  "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).

json
{
  "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.

FieldTypeDefaultDescription
durationInteger20Ticks from stage 0 to the end of stage 9.
on_finishString"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_itemsBooleantrueWith on_finish: break: whether the block drops its items.
clearBooleanfalseWhen 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.

json
{
  "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.

FieldTypeDefaultDescription
entity_typeIdentifierrequiredWhat it looks like, like minecraft:zombie. Any type except minecraft:player. It uses the type's default look.
x, y, zDouble0Where it spawns.
relative_toString"self"self: x/y/z are an offset from the entity running the action, like ~ ~ ~. world: they're world coordinates.
behaviorString"neutral", "stationary" for fake playersHow it moves. See below.
lifetimeIntegerrequiredTicks until it disappears.
viewer_conditionBi-entity conditionnoneWho can see it. The actor is the viewing player and the target is the entity that spawned it. Left out: every player nearby.
recheckBooleantruetrue: 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.
idIntegerassignedA 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.
skinStringnoneOnly 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:

behaviorWhat it does
hostileChases the nearest player who can see it, within the mob's follow range.
neutralWanders around and glances at nearby viewers.
passiveWanders like neutral until a viewer hits it, then chases that player.
stationaryNever 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 passive phantom chase you and runs its spawner's action_on_phantom_hit powers.
  • 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 lifetime runs 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.

json
{
  "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.

json
{
  "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 is stationary; 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.

TypeKindWhat it does
phantom_actionentity actionruns actions on your phantoms, or one by id
phantomentity conditioncounts your phantoms, or checks one by id
action_on_phantom_hitpowerruns when a viewer hits one of your phantoms
/tma phantom runcommandruns 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. Use despawn_phantom instead.
  • 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.

FieldTypeDefaultDescription
idIntegernoneThe phantom to act on, whoever owns it. Left out: every phantom the running entity spawned.
phantom_conditionEntity conditionnoneOnly phantoms that pass this.
entity_actionEntity actionnoneRuns on the phantom.
bientity_actionBi-entity actionnoneThe actor is the entity running the action and the target is the phantom.

Example: your phantoms glow for 5 seconds.

json
{
  "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.

FieldTypeDefaultDescription
idIntegernoneOnly the phantom with this id, whoever owns it. Left out: every phantom this entity spawned.
entity_conditionEntity conditionnoneChecked on the phantom.
bientity_conditionBi-entity conditionnoneThe actor is this entity and the target is the phantom.
comparisonComparison">="
compare_toInteger1

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.

json
{
  "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.

FieldTypeDefaultDescription
idIntegernoneOnly hits on this phantom. Left out: any of the holder's phantoms.
entity_actionEntity actionnoneRuns on the holder.
bientity_actionBi-entity actionnoneThe actor is the player who hit it and the target is the phantom.
bientity_conditionBi-entity conditionnoneSame actor and target. Only runs when this is true.
conditionEntity conditionnoneChecked on the holder, like any power.
  • It runs for every behavior, and a passive phantom 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.

json
{
  "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.

FieldTypeDefaultDescription
idIntegernoneThe phantom to remove. Left out: every phantom this entity spawned.
  • Ids are unique across the server, so an id removes that phantom whoever spawned it. Without an id, only the running entity's own phantoms go.
  • Phantoms from /tma phantom get 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.

json
{
  "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.

FieldTypeDefaultDescription
entity_typeIdentifierrequiredWhat it looks like, like minecraft:zombie. Any type except minecraft:player. It uses the type's default look.
durationInteger0Ticks until the disguise ends. 0 or less: until undisguise or the entity is gone.
viewer_conditionBi-entity conditionnoneWho 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.
recheckBooleantruetrue: 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.

json
{
  "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.

json
{ "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.

IdRuns whenHolderOther entity (bi-entity target)
too-many-attributes:action_on_deaththe holder diesany living entitythe killer, if there is one
too-many-attributes:action_on_jointhe holder joins the servera playernone
too-many-attributes:action_on_disconnectthe holder leaves the servera playernone
too-many-attributes:action_on_start_trackingthe server starts sending an entity to the holdera playerthat entity
too-many-attributes:action_on_mob_conversionthe holder turns into another moba mobthe new mob
too-many-attributes:action_on_sleep_startthe holder falls asleepa player or villagernone

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:

FieldTypeDefaultOnDescription
entity_actionEntity actionoptionaldeath, join, disconnect, sleep_startRuns on the holder.
bientity_actionBi-entity actionoptionaldeath, start_tracking, mob_conversionRuns 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_conditionBi-entity conditionoptionaldeath, start_tracking, mob_conversionIf set, both actions only run when it's true. Nothing runs if there's no other entity.
damage_conditionDamage conditionoptionaldeathIf set, both actions only run when the killing damage matches. The amount is the damage before armor.

What each event means exactly:

  • action_on_death runs 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, use origins:prevent_death). The "killer" is the entity credited by the damage source: the shooter of an arrow, not the arrow.
  • action_on_join runs once the player is fully in the world, with their powers loaded. That's every login, not just the first one.
  • action_on_disconnect runs while the player is leaving, before their data is saved, so variable changes made here are kept.
  • action_on_start_tracking is 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's entity-broadcast-range-percentage and 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_conversion covers 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_start runs 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 an origins:prevent_sleep power stop it from firing. (Suspected from how Fabric's event is called; not yet checked in-game.) Origins' origins:action_on_wake_up covers the other end. To check "asleep right now", use is_sleeping.

Example: "Last words". When you die to another player, they get Glowing for 30 seconds.

json
{
  "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:

FieldTypeDefaultDescription
signalStringrequiredThe name, like mypack:war_cry. Without a namespace it gets too-many-attributes:. Names are lowercased.
radiusDoublenoneOnly entities within this many blocks, in the same dimension, hear it. Left out: every loaded entity in every dimension.

Listening:

FieldTypeDefaultDescription
signalsArray of StringsrequiredThe names to listen for. * matches any run of characters: mypack:*, *:alarm, or * for everything.
entity_actionEntity actionoptionalRuns on the listener.
bientity_actionBi-entity actionoptionalActor: the listener. Target: the entity that sent the signal.
bientity_conditionBi-entity conditionoptionalBoth 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.

json
{
  "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.

json
{
  "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

FieldTypeDefaultDescription
blockBlock staterequiredWhat viewers see, like minecraft:stone or minecraft:oak_door[facing=east,half=upper].
x, y, zDouble0Entity action only: the position.
relative_toString"self"Entity action only: self (an offset from the entity) or world.
viewer_conditionConditionnoneWho 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.
recheckBooleantrueRe-check viewer_condition twice a second, so players start and stop seeing it. false keeps each player's first result.
durationInteger0Ticks 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_block takes 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.

json
{
  "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.

FieldTypeDefaultDescription
blockBlock staterequiredWhat the matching blocks look like.
block_conditionBlock conditionnoneReplace mode: every real block within radius of the holder matching this condition is shown as block.
radiusInteger8Replace mode only. Capped at 16.
offsetsArray of {x, y, z}noneOffset mode: positions relative to the holder, which move with them.
rotate_with_facingBooleanfalseOffset mode: turn the offsets with the holder's facing, like ^ ^ ^ in commands (x left, y up, z forward).
show_to_holderBooleantrueWhether the holder sees them.
viewer_conditionBi-entity conditionnoneOther players who also see them: actor the viewing player, target the holder. Left out: nobody else.
intervalInteger10Ticks between recomputes.
  • Replace mode shows the holder only, unless viewer_condition adds 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 interval if you use a big radius.
  • The power's condition turns 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.

json
{
  "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.

json
{
  "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.

FieldTypeDefaultDescription
substringsArray of StringsrequiredThe text to look for. Each one can be anywhere in the message.
matchString"any"any: one substring is enough. all: every substring must be in the message.
orderedBooleanfalseWith match: all: the substrings must appear in the order listed, without overlapping.
case_sensitiveBooleantrueWhen false, capital letters don't matter.
directionString"both"outgoing: messages the holder sends. incoming: messages other players send while the holder is online. both: either.
hide_messageBooleanfalseFor a matching outgoing message: nobody sees it in chat. It has no effect on incoming messages.
entity_actionEntity actionoptionalRuns on the holder, once per matching message.
bientity_actionBi-entity actionoptionalActor: the holder. Target: for an outgoing message, each other online player (once each); for an incoming one, the sender.
bientity_conditionBi-entity conditionoptionalFilters 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, /tell and 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: both can'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.

json
{
  "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.

json
{
  "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:

mcfunction
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 10

The 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 /attribute and execute 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 in hud_render conditions and other client-checked places (see page 1).

    json
    { "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 1 or 0.
  • Values are clamped to the range below. For example, time_since_last_meal stops at 1,000,000,000.
  • Don't change them. The base value is overwritten every tick. Modifiers added with /attribute … modifier add would stay, and would make get return a wrong value. (Exception: drag - it's meant to be changed.)

The list

Attribute (too-many-attributes:…)ValueRangeCondition
attack_cooldown_progresshow charged the attack is0 – 1attack_cooldown_progress
hurt_timehurt animation ticks left0 – 40hurt_time
invulnerable_ticksinvulnerability ticks left0 – 40invulnerable_ticks
velocity_x / velocity_y / velocity_zvelocity, blocks per tick−16 – 16velocity
look_pitchup/down look angle−90 – 90look_angle
look_yawleft/right look angle−180 – 180look_angle
is_jumping1 while jumping0 / 1is_jumping
crouch_depthsneak transition0 – 1crouch_depth
exhaustionfood exhaustion0 – 40exhaustion
xp_progressprogress to next level0 – 1xp_progress
biome_temperaturebiome base temperature−2 – 2biome_temperature
regional_difficultylocal difficulty0 – 6.75regional_difficulty
light_at_eyeslight level at eye height0 – 15light_at_eyes
last_attack_reachdistance of your last attack0 – 32last_attack_reach
recent_damage_takendamage taken in the last 60 ticks0 – 2048recent_damage_taken (whose window you can change)
recent_damage_blockeddamage blocked with a shield in the last 60 ticks0 – 2048recent_damage_blocked (whose window you can change)
vibration_cooldownvibration cooldown ticks left0 – 2400vibration_on_cooldown
vibration_on_cooldown1 while on cooldown0 / 1vibration_on_cooldown
is_blocking1 while blocking with a shield0 / 1is_blocking
critical_hit_eligible1 while a hit would crit0 / 1critical_hit_eligible
last_damage_amountyour last hit's damage0 – 2048last_damage_amount
recent_knockbackyour last knockback's strength0 – 16recent_knockback
time_airborneticks since last on the ground0 – 72000time_airborne
sprint_ticksticks sprinting without a break0 – 72000sprint_ticks
submersion_depthfluid blocks above your eyes0 – 16submersion_depth
glide_ratioforward ÷ downward speed while gliding (0 otherwise)0 – 1000glide_ratio
time_since_last_mealticks since you last ate0 – 1,000,000,000time_since_last_meal
distance_to_nearest_playerblocks to the closest other player0 – 1,000,000distance_to_nearest_player
last_death_distanceblocks to where you last died (the recovery compass's target); −1 if you've never died or died in another dimension−1 – 100,000,000none; in code, Death.distance()
redstone_powerredstone signal reaching the block at your feet0 – 15redstone_power
moon_phasemoon phase0 – 7moon_phase
frictioneffective slipperiness of the block you move on0 – 2friction
is_moving_loudly1 while moving loudly0 / 1is_moving_loudly
mainhand_cooldownvanilla cooldown ticks left on the item in your main hand0 – 1,000,000,000item_on_cooldown
voice_installed1 with Simple Voice Chat connected0 / 1voice_installed
voice_mic_disabled1 with voice chat disabled0 / 1voice_mic_disabled
is_talking1 while talking0 / 1is_talking
is_whispering_voice1 while whispering0 / 1is_whispering
in_voice_group1 while in a voice group0 / 1in_voice_group
is_broadcasting_voice1 while broadcasting0 / 1is_broadcasting_voice
is_muted_voice1 while force-muted0 / 1is_muted_voice
is_deafened_voice1 while force-deafened0 / 1is_deafened_voice
is_downed1 while downed (needs Hardcore Revival)0 / 1is_downed

Two conditions map to more than one attribute:

  • The velocity condition's axes are the three velocity_* attributes.
  • look_angle is split into look_pitch and look_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.

mcfunction
attribute @s too-many-attributes:drag base set 0.9

Falling 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.

mcfunction
attribute @s too-many-attributes:drag base set 0.995

Falling now caps around 15.92 blocks/tick - about four times vanilla's terminal velocity, still with the same gradually-accelerating feel.

dragFall caps aroundFeel
0.0never builds speedhovers, no fall at all
0.50.08 blocks/tickvery heavy air resistance
0.90.72 blocks/tickgentle, slow-falling-like
0.98 (default)3.92 blocks/tickvanilla
0.99515.92 blocks/tickfast, still capped
1.0never capstrue 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 /attribute modifiers the normal way: add an ADD_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 with modifier 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 - drag only 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.

IdKindWhat it does
variablespower typedeclares variables
variable_barpower typelinks one number variable to Origins' resource system and a HUD bar
executeentity and bi-entity actionruns code that changes variables
modify_variableentity and bi-entity actionone change, written as JSON like change_resource
expressionentity and bi-entity conditionchecks 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:

json
{
  "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:

json
{
  "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:

json
{
  "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:

json
{
  "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.

FieldTypeDefaultDescription
fieldsString or array of stringsrequiredJava field declarations, like int mana = 100, maxMana = mana;
java
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, index and size (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 a String starts 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, so int x = 1.5; is a load error; write int 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 fields in a pack update: variables that still exist keep their saved values. A number variable whose type changed has its value cast, for example from int to double. Anything that can't carry over resets to its initializer.
  • Names must be unique per entity across all the variables and list powers it holds. If two powers declare the same name, code that uses it fails with variable 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.

FieldTypeDefaultDescription
variableStringrequiredName of an int, long, float or double variable
minIntegerrequiredThe value at which the bar is drawn empty
maxIntegerrequiredThe value at which the bar is drawn full
hud_renderHUD renderrequiredHow the bar looks, as for origins:resource

What works through the bar's id:

  • The origins:resource condition reads the variable.
  • origins:change_resource and origins:modify_resource write the variable.
  • The HUD bar shows it.
  • The resource field of an Origins modifier reads it (page 9).
  • (Suspected) Origins' /resource command 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 long or double beyond the int range reads as Integer.MAX_VALUE or MIN_VALUE.
  • Decimals are cut off: a double of 7.9 reads as 7.

Example: use Origins' own actions on a variable.

json
{
  "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.

FieldTypeDefaultDescription
codeString or array of stringsrequiredStatements, each ending in ;
json
{ "type": "too-many-attributes:execute", "code": "mana -= 30; casting = true; mode = \"fire\";" }
  • A statement must be an assignment (=, +=, …), a ++/--, or a method call like history.add(5). A bare expression such as mana + 5; is an error, as in Java, because you almost certainly meant mana += 5;.
  • There's no if, loop or local variable. Use ?: for choices:

    java
    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.

FieldTypeDefaultDescription
variableStringrequiredA variable name. In the bi-entity version also self.name or other.name.
operationString"add"See the table below
valueNumber, Boolean or StringrequiredA 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.
operationSame as the Java statement
setx = value
addx += value (joins Strings too)
subtract / multiply / divide / modulox -= value / x *= value / x /= value / x %= value
min / maxx = Math.min(x, value) / x = Math.max(x, value)
and / or / xorx &= value / x |= value / x ^= value. On a boolean, xor with true toggles it.
shift_left / shift_right / unsigned_shift_rightx <<= value / x >>= value / x >>>= value
json
{ "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;:

json
{
  "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:

  • set only widens, so set with 1.5 on an int is an error, just like =.
  • Every other operation casts the result back to the variable's type, like Java's compound assignment. min and max do too: max with 9.9 on an int gives 9.

Expression

too-many-attributes:expressionentity condition and bi-entity condition

True when a Java expression is true.

FieldTypeDefaultDescription
expressionString or array of stringsrequiredOne expression that produces a boolean
json
{ "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 false and the error is logged.
  • A condition Origins checks on the client, like a hud_render condition, 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.name is the actor: Origins' first entity, usually the power holder.
  • other.name is the target: Origins' second entity.
  • A bare name means self.name.

Example: drain 10 mana from whoever you hit.

json
{
  "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 as false.
  • 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_towith_variables
Use variables in chat, command blocks or functions/tma with
Read a scoreboard score in code, for example a value shared by a teamScoreboard.get
Read what item is in a slot: a vanilla slot, or one of your origins:inventory powers, for you or the other entityItem.id and Item.count
Feed a number to a power field that's read continuously, such as a modifiervariable_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.

FieldTypeDefaultDescription
actionEntity action type (or bi-entity action type)required in the action versionsThe action to run
conditionEntity condition type (or bi-entity condition type)required in the condition versionsThe 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). A double prints as 3.0, a long without an L.
  • $${ 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_read loop: ${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.

json
{
  "type": "too-many-attributes:with_variables",
  "action": {
    "type": "origins:add_velocity",
    "y": "${jumpPower * 0.1}",
    "space": "world"
  }
}

A String into origins:execute_command:

json
{
  "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.

json
{
  "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.

json
{
  "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.

json
{
  "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 as false.
  • Cast for whole-number fields, like duration. (Suspected: a decimal read as a whole number is cut off, e.g. 2.9 becomes 2.)
  • Quotes in String variables. A String is inserted exactly as it is. If it contains " and lands inside a tellraw JSON text, that JSON breaks. Use name.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 (a double that 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:

mcfunction
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, like execute as, so @s is the target. Position, dimension and permission level stay those of whoever ran /tma with. For the target's position, use tma with @a execute at @s run ….
  • Everything becomes text. A command is always text, so every placeholder uses Java's text conversion. ${d} for a double prints 3.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 with fails 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 /tma commands. 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 ${…}.

CallReturns
Scoreboard.get(objective)intthis entity's score in objective
Scoreboard.get(holder, objective)intthe score of any holder: a player name, or a fake name like #bank
Scoreboard.has(objective)booleanwhether this entity has a score in objective
Scoreboard.has(holder, objective)booleanwhether holder has a score in objective
java
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 is false. An unknown objective is an error too. Check with Scoreboard.has first 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 @s or @p don't work here.
  • It only reads. To write a score, run a command with the value: /tma with or with_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:

json
{
  "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:

mcfunction
execute as @a store result score @s sprint run attribute @s too-many-attributes:sprint_ticks get
java
longestSprint = 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:

json
{
  "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

FieldContainsCan change things?
fields of variablesfield declarationsonly its own initial values
code of executestatementsyes
code of for_eachstatementsyes, including value
expression of expressionone boolean expressionno
value of modify_variableone expressionno
${…} in with_variables and /tma withone expressionno

Names

NameMeans
manathis entity's variable (the actor, in bi-entity code)
self.manathe same, written out
other.manathe target's variable. Only in bi-entity code.
Scoreboard, Item, Death, Stat, Globalbuilt-in names; they can't be variable names
value, index, sizethe 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:

OperatorsNotes
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 / 2 is 2, and 5 / 2.0 is 2.5.
  • int and long overflow wraps around.
  • = only widens. int x = someDouble; is an error; write x = (int) someDouble;.
  • Compound assignment casts for you, so x += 1.5 on an int is allowed.
  • Casts between number types behave like Java's. Booleans and Strings can't be cast to or from numbers. Use String.valueOf(x), "" + x or Integer.parseInt(s).

Built-ins

GroupMembers
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 methodslength() 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 methodssize() 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) stays int
  • Math.max(int, long) is long
  • Math.round(double) is long
  • Math.round(float) is int

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).

MethodTypeValue
Death.has()booleanwhether there's a death location. Always false for non-players.
Death.x(), Death.y(), Death.z()intthe block where they died. An error without a death location, so check Death.has() first.
Death.dimension()Stringthe dimension they died in, like minecraft:the_nether. Same error rule.
Death.distance()doubleblocks 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_distance attribute.

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 stat condition for the list of types.

Example: blocks walked per hour played, as a double:

java
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, byte or short.
  • No control flow (if, for, while, return), no local variables, no new, 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 promote c ? 1 : 2.0 to double either way.
  • toUpperCase() and toLowerCase() ignore the system language.
  • No octal numbers: 010 is 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 as false.

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.

IdKindWhat it does
listpower typedeclares one list
power_listpower typea read-only list of the holder's current power ids
item_listpower typea read-only list of the item ids in one of the holder's origins:inventory powers
for_eachentity and bi-entity actionruns code for each element; value can be changed
for_each_readentity and bi-entity actionruns 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.

FieldTypeDefaultDescription
variableStringrequiredThe name code uses. Same rules as variable names, and unique on the entity across all variables and list powers.
element_typeStringrequiredint, long, float, double, boolean or String. Every element has this type.
initialArray[]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_sizeInteger1024The most elements the list can hold
when_fullString"error"What add does on a full list: "error" fails, and "drop_oldest" removes element 0 first, which makes a rolling log.
json
{
  "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.

MethodReturnsNotes
history.size()int
history.isEmpty()boolean
history.get(i)the elementerror if i is outside 0 … size()-1
history.contains(v) / history.indexOf(v)boolean / intindexOf is -1 when missing
history.set(i, v)the old elementchanges the list
history.add(v)truechanges the list; follows when_full
history.add(i, v)nothinginserts at i; changes the list
history.remove(i)the removed elementan int argument removes by position
history.remove(v)booleanany other argument removes the first equal element
history.clear()nothingchanges the list
java
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_variable values and ${…}, just like assignments.
  • A list isn't a value: x = history; or history + 1 fails 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:

NameType
valuethe element typethe current element
indexintits position, starting at 0
sizeintthe list's size
  • index and size are always read-only.
  • value can be changed only directly in a for_each code block. Assigning it anywhere else is a load error.
  • value, index and size can'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, remove and clear on it fail, like Java's ConcurrentModificationException. 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.

FieldTypeDefaultDescription
listStringrequiredThe list: cooldowns, or other.cooldowns in the bi-entity version
codeString or array of stringsrequiredStatements, run once per element

Example: count every ability cooldown down by one each tick.

json
{
  "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.

FieldTypeDefaultDescription
listStringrequiredThe list: history, or other.history in the bi-entity version
actionEntity action typerequired in the entity versionRun once per element
bientity_actionBi-entity action typerequired in the bi-entity versionRun 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.

json
{
  "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.

json
{
  "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.

FieldTypeDefaultDescription
variableStringrequiredThe name code uses. Same rules as list.
include_hiddenBooleanfalseAlso list hidden powers. (Suspected: parts of origins:multiple powers are usually hidden, so the default is roughly what a player sees.)
only_activeBooleanfalseLeave out powers whose own condition is false right now
tagStringnoneOnly powers in this power tag. Without a namespace it gets this power's own.
sourceIdentifiernoneOnly 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:

json
{ "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:

json
{ "type": "too-many-attributes:expression", "expression": "powers.contains(\"origins:elytra\")" }
json
{
  "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.

FieldTypeDefaultDescription
variableStringrequiredThe name code uses. Same rules as list.
powerIdentifierrequiredThe origins:inventory power to read
countsBooleanfalsefalse lists item ids as Strings; true lists stack sizes as ints
json
{ "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:inventory power, the list is empty and the reason is logged once.
  • Like every computed list it's read-only: add, set, remove and clear fail when the code runs. Use the actions on page 14 to change items.

Example: count how many slots hold logs.

json
{
  "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?

json
{
  "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 an int element by value, use remove(indexOf(v)).
  • add/set widen like =: 3 goes into a double list as 3.0. Java's generics would reject it.
  • contains/indexOf compare like ==, so 3 matches 3L. Java's equals wouldn'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_each writes value back 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 gets too-many-attributes:, so "score" is too-many-attributes:score. Keys use lowercase letters, digits and _ - . /.
  • Typed like variables: int, long, float, double, boolean or String, 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_render condition can't read them (it counts as false and logs an error).

There are four ways in:

WayUse it for
Global.* in codeany key, including keys built while the code runs
global_variables powerfixed, declared fields that every holder uses by plain name
modify_global and delete_global actionsJSON, like modify_variable
/tma globallooking 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.

MethodReturns
Global.has(key)booleanwhether the key exists
Global.get(key)the valuean error if the key doesn't exist or is a list
Global.set(key, value)the valuecreates the key with the value's type, or sets an existing one
Global.delete(key)booleanremoves 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 listan existing global list; use it with list methods: Global.list(key).size()
Global.list(key, type)a listthe same, but creates an empty list of type ("int", "String", …) if it's missing
  • set, delete, bind, lock, unlock and list(key, type) change the store, so they only work in actions (execute, for_each), not in conditions or modify_variable values. 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).
  • Global is 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.

json
{
  "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:

json
{ "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.

FieldTypeDefaultDescription
fieldsString or array of StringsrequiredJava field declarations: int faction_score = 0; boolean gate_open;
lockBooleanfalseWhen true, keys this power creates are locked to its namespace.
  • The key for field name in power mypack:whatever is mypack:name, in lowercase. Any power in the mypack namespace 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 variables and a global_variables power the entity holds.
  • variable_bar and 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.

json
{
  "type": "too-many-attributes:global_variables",
  "fields": "int faction_score = 0;"
}
json
{
  "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.

FieldTypeDefaultDescription
keyStringrequiredThe key.
operationString"set"set, add, subtract, multiply, divide, modulo, min, max, and, or, xor, shift_left, shift_right, unsigned_shift_right
valueStringrequiredAn expression, like "1" or "coins / 10". It can read variables and globals.
bindString"none"After the change, bind the key to self, other (bi-entity only), or both.
  • set creates a missing key. Any other operation needs the key to exist already.

Delete global

too-many-attributes:delete_globalentity action and bi-entity action

FieldTypeDefaultDescription
keyStringrequiredThe key to remove. Nothing happens if it doesn't exist.

Example: turn on a world flag the first time anyone enters the Nether.

json
{
  "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, lock and unlock. 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_variables with "lock": true locks the keys it creates to its namespace.
  • /tma global commands 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) (or bind on modify_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, default 30, 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

mcfunction
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>
SubcommandWhat it doesResult
getprints the valuethe value if it's a whole number, 1/0 for booleans, the size for a list, otherwise 1
setsets 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
deleteremoves a key1
listevery 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 tothe 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 tickshow many were deleted
lock / unlocklocks a key to an owner, or removes the lock1
  • 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.

IdKindNeeds SVC?
voice_installedentity conditionyes
voice_mic_disabledentity conditionyes
is_talkingentity conditionyes
is_whisperingentity conditionyes
in_voice_groupentity conditionyes
in_same_voice_groupbi-entity conditionyes
in_voice_range_ofbi-entity conditionyes
is_broadcasting_voiceentity conditionno (it reads TMA's own state)
is_muted_voiceentity conditionno (it reads TMA's own state)
is_deafened_voiceentity conditionno (it reads TMA's own state)
start_broadcast_voiceentity actionto be heard, yes
stop_broadcast_voiceentity actionno
/tmavccommandto 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.

json
{
  "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 mute doesn't count as talking.

Example: music notes while you speak.

json
{
  "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:

json
{
  "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.

json
{
  "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.

FieldTypeDefaultDescription
max_distanceDouble-1The range in blocks. Any negative value uses the voice distance set in SVC's server config.
  • False without SVC, whatever max_distance is.

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:

json
{
  "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.

FieldTypeDefaultDescription
listenersBi-entity condition typerequiredChecked for each online player, each time audio is sent. The actor is the broadcaster and the target is the possible listener.
ticksInteger-1How 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_of inverted to leave out players who can already hear them.
  • The broadcaster never hears themselves, and force-deafened listeners hear nothing.
  • Because listeners is 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.

json
{
  "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.

mcfunction
tmavc broadcast start [<listeners>]
tmavc broadcast stop
tmavc mute start <targets>
tmavc mute stop <targets>
tmavc deafen start <targets>
tmavc deafen stop <targets>
SubcommandWhat it does
broadcast startYou broadcast to every online player, or only to the <listeners> players. It lasts until broadcast stop. Must be run by a player.
broadcast stopEnds your broadcast
mute start / stopForce-mutes or unmutes the target players
deafen start / stopForce-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…UseReaches
Check or change your own power inventory from power JSONOrigins' own types, belowonly 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.0container.<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:

TypeField that selects power inventoriesPlus
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 power but no inventory_type(s), the player's normal inventory is used. With inventory_type(s) but no power, 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 powerinv for that.

Example: does slot 0 of mypack:satchel hold at least 1 item?

json
{
  "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.

json
{
  "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.

mcfunction
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.3
  • entity <target> <slot>: any vanilla slot, e.g. entity @s hotbar.0 or entity @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.

SubcommandWhat it doesResult (for execute store result)
getShows the stack in the slotthe item count, or 0 if empty
idPrints the item's id on its own, as plain text: minecraft:spruce_log, or minecraft:air for an empty slotthe item's number in the item registry
setReplaces the slot with a new stackthe count set
clearEmpties one slot, or the whole power if no slot is givenitems removed (one slot) or stacks removed (whole power)
copyCopies the source stack over the destination. Overwrites anything already there, and the source keeps its item.the count copied
moveMoves 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:

mcfunction
execute store result score @s satchel_0 run tma powerinv get @s mypack:satchel container.0

Hand one player's slot 0 to another player's matching slot:

mcfunction
tma powerinv move power @s mypack:satchel container.0 power @p[tag=partner] mypack:satchel container.0

Pull a power slot into the hotbar, or put the held item into the power:

mcfunction
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.4

Reading item types

Three ways to find out what is in a slot, not just how many:

FromUse
Variable code, conditions, ${…}Item.id(power, slot) and Item.count(power, slot)
Power JSON, as a list you can loop overtoo-many-attributes:item_list
Commands and functionstma powerinv id

Item.id and Item.count

java
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");
CallReturns
Item.id(power, slot)Stringthe item in a power inventory slot: its full id, namespace:item. An empty slot is minecraft:air.
Item.count(power, slot)intthe stack size there, 0 when empty
Item.id(slot)Stringthe item in a vanilla slot, named as in /item
Item.count(slot)intthe 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) is container.3.
  • Vanilla slots: weapon.mainhand, weapon.offhand, armor.head, armor.chest, armor.legs, armor.feet, hotbar.0hotbar.8, inventory.0inventory.26, enderchest.0enderchest.26. The short forms mainhand, offhand, head, chest, legs and feet work 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 self or other first to choose: Item.count(other, "weapon.mainhand"). other only exists in bi-entity actions and conditions, as elsewhere in the language (page 8).
  • They're read-only, so they work in execute code, expression conditions, modify_variable values 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 as false. The message says which.

Example: in a bi-entity action, take note of what the entity you hit is holding.

json
{
  "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.

json
{
  "type": "too-many-attributes:expression",
  "expression": "Item.id(\"mypack:satchel\", 0).endsWith(\"_log\") || Item.count(\"mypack:satchel\", 0) == 0"
}

Item.cooldown

java
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
CallReturns
Item.cooldown(itemOrSlot)intticks 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 in Item.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_cooldown condition. Set it with set_item_cooldown.

tma powerinv id

mcfunction
tma powerinv id @s mypack:satchel container.0

prints exactly:

text
minecraft:spruce_log

Its result is the item's number in the item registry, so a score can follow a slot's item without any text handling:

mcfunction
execute store result score @s slot0_item run tma powerinv id @s mypack:satchel container.0

That 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:

java
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.

CommandWhat 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

mcfunction
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 ${.
mcfunction
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

mcfunction
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.

SubcommandResult
getthe item count in the slot
idprints the item id as plain text (minecraft:spruce_log); returns its registry number
setthe count set
clearitems removed from one slot, or stacks removed from the whole power
copythe count copied (overwrites the destination)
movethe count moved (refuses a non-empty destination)

Full details and examples are on page 14.


/tma phantom

mcfunction
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 list

Spawns 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/stationaryits 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.
SubcommandResult
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
listevery live phantom with its id, type, position, ticks left and viewer count; returns how many
  • <viewers> is decided once, when the command runs, like viewer_condition with recheck: 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: spawn and skin return the phantom's id, and their feedback says it (Spawned phantom 3: …). Store it with execute store result to 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, not spawn minecraft:player: players aren't a summonable type, so spawn refuses them.
mcfunction
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 list

The 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:

mcfunction
tma with @s tma phantom remove ${Scoreboard.get("stalker")}

/tma disguise

mcfunction
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, like viewer_condition with recheck: false.
  • Disguising an entity that's already disguised replaces the disguise.
  • Result: disguise returns the number of targets; undisguise returns how many were disguised. disguise fails for players and types that can't be summoned.
mcfunction
tma disguise @s minecraft:creeper 600
tma disguise @e[type=pig,distance=..10] minecraft:ghast 0 @a[team=red]
tma undisguise @a

/tma global

mcfunction
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:.

SubcommandResult
getprints the value; returns it if it's a whole number, 1/0 for a boolean, the size of a list, otherwise 1
setsets or creates the key from a Java literal (5, 2.5, true, "text"); returns the new value if it's a number, otherwise 1
deleteremoves the key; fails if it doesn't exist
listprints 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 / unlocklocks the key to an owner (a namespace or power id), or removes the lock
mcfunction
tma global set mypack:gate_open true
tma global list mypack:bond/
execute store result score #kills stats run tma global get mypack:zombie_kills

Full details are on page 12.


/tma fakeblock

mcfunction
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: set returns how many players it was shown to; clear returns 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.
mcfunction
tma fakeblock set ~ ~ ~2 minecraft:bedrock @a[team=red] 400
tma fakeblock clear 100 64 -20

/tma powertag

mcfunction
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.

mcfunction
tma powertag list mypack:magic
tma powertag of origins:phantomize

/disableshield

mcfunction
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:

json
{
  "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.

mcfunction
tmavc broadcast start [<listeners>]
tmavc broadcast stop
tmavc mute start <targets>
tmavc mute stop <targets>
tmavc deafen start <targets>
tmavc deafen stop <targets>
SubcommandWhat it does
broadcast startYou (the player running the command) broadcast your voice to every online player, or only to <listeners> if given. It lasts until broadcast stop.
broadcast stopEnds your broadcast
mute start / stopForce-mutes the targets: their microphone audio reaches nobody. Separate from the player's own mic toggle.
deafen start / stopForce-deafens the targets: they hear no voice audio at all.
  • broadcast must be run by a player.
  • Result: broadcast returns 1; mute and deafen return 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.

json
{
  "movement": [
    "mypack:double_jump",
    "mypack:wall_climb",
    "gravity-shifter:*"
  ],
  "otherpack:unsilenceable": [
    "mypack:heartbeat"
  ]
}
  • * matches any run of characters, so gravity-shifter:* is every power in that pack, and mypack: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 tag mypack: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.

FieldTypeDefaultDescription
tagStringrequiredThe 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_inactiveBooleanfalseBy 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.

json
{
  "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.

json
{
  "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:

json
{
  "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

mcfunction
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.

SubcommandWhat it printsResult
listevery tag and how many loaded powers it coversthe number of tags
list <tag>the powers in that tag, and the entries it was written withhow many powers
of <power>every tag that power is inhow many tags
mcfunction
tma powertag list mypack:magic
tma powertag of origins:phantomize

Tip: 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.)

RecipeUses
Ragevariables, variable_bar, recent_damage_taken, modifier resource
Combo countervariables, bi-entity execute, expression, with_variables
Team bankvariables, Scoreboard.get, with_variables, /tma with
Loud footstepsis_moving_loudly, vibration_on_cooldown, set_vibration_cooldown
Radiostart_broadcast_voice, stop_broadcast_voice, is_broadcasting_voice
Linked inventoryItem.id, Item.count, list, for_each, for_each_read, with_variables, /tma powerinv copy
BondGlobal.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:

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.

json
{
  "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.

json
{
  "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.

json
{
  "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.

json
{
  "type": "origins:modify_damage_dealt",
  "modifier": { "operation": "add_base_early", "value": 0, "resource": "mypack:rage_damage_bar" }
}

How it fits together:

  • recent_damage_taken with a 20-tick window is "was I hurt in the last second".
  • The code keeps rageDamage in step with rage, using whole-number division.
  • The modifier reads rageDamage through 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:

json
{
  "type": "too-many-attributes:variables",
  "fields": ["int combo = 0;", "int comboTimer = 0;"]
}

combo_hit.json: count hits.

json
{
  "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.

json
{
  "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:

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):

mcfunction
scoreboard objectives add team_bank dummy

wallet.json:

json
{
  "type": "too-many-attributes:variables",
  "fields": ["long coins = 100;"]
}

deposit.json: press your primary key to deposit up to 10 coins.

json
{
  "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:

mcfunction
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:

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:

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:

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.

json
{
  "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.

json
{
  "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.

json
{
  "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.

json
{
  "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, and minecraft:air x0 for 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_read walks the snapshot and compares each slot with the fingerprint, using index as 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_each then writes the current fingerprints back into the snapshot, so each change pushes once.

Worth knowing before you build on it:

  • copy overwrites. 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 with Item.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.

json
{
  "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.

json
{
  "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.

json
{
  "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.

json
{
  "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_id must exist, and the expression fails (counts as false) for anyone without bond_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.