Chapter 36 — Damage Types
Part X — Advanced: Data-Driven Systems. Like the rest of Part X, this chapter is à la carte: read it when you want it. It assumes you’re comfortable with registry tags (Chapter 14) and with the two item components that have been quietly waiting on it:
blocks_attacks(Chapter 23) anddamage_resistant(Chapter 24).
What You’ll Build
Back in Chapter 23 you turned items into shields with the blocks_attacks component, and two of
its fields (bypassed_by and the type inside damage_reductions) pointed at something called
a “damage type tag” that we promised to explain in Chapter 36. In Chapter 24 the damage_resistant
component did the same thing with its types field. This is Chapter 36. By the end of it, you’ll
know exactly what those fields were pointing at, and you’ll be able to make your own.
A damage type is a named kind of damage with its own properties: how much hunger it drains,
whether it gets stronger on Hard difficulty, which hurt animation it shows, and what death message
it writes in chat. The game’s own “arrow damage,” “lava damage,” and “fall damage” are all damage
types, each defined in a small JSON file. In this chapter you’ll write your own damage type called
void touch, learn the /damage command that applies it, group damage types with damage type
tags, and use a tag to make your void-touch damage ignore armor entirely. Everything goes into
the running mypack data pack from Chapter 9, and you’ll test it in the Chapter 8 test world.
What a damage type actually is
Here’s the plain definition: damage types are JSON files located in data packs that define different kinds of damage that entities can take. They control which attributes the damage has as well as which death message is used when an entity dies due to that type of damage.
So a damage type is data, just like a recipe or a loot table. It lives at a predictable path:
data/<namespace>/damage_type/<name>.json
That means it follows the same friendly rule as the other data-pack files you’ve written since
Chapter 9: when you change one and run /reload, the game picks it up right away. (Contrast that
with enchantments back in Chapter 35, which are a dynamic registry and need a full world reload to
change. Damage types are the easy kind: edit, /reload, done.)
One important limit: custom damage types can be applied only by using
the /damage command. A custom damage type doesn’t automatically attach itself to anything: no
block, no mob, no item starts dealing it on its own. The only way to make your damage type actually
hurt something is the /damage command, which you’ll meet in a moment. That’s by design: it means
you decide exactly when your custom damage happens.
The damage type file, field by field
The damage type file has a small, friendly set of fields. Here is every one of them.
The file is a single JSON object (a { } with key-value pairs inside, like every data-pack file
since Chapter 8). Its fields:
message_id— a string. It’s used as part of the death message translation key when an entity dies to this damage type anddeath_message_typeis set todefault(the default, see the field below). (A death message is the line like “Steve was slain by a zombie” that appears in chat when something dies.) More on this just below.exhaustion— a number (it can have a decimal). The amount of hunger exhaustion this damage causes. Exhaustion is the hidden meter that, when it fills up, eats into your saturation and then your hunger bar, so a highexhaustionvalue makes this damage also make you hungry.scaling— a string controlling whether the damage gets bigger on harder difficulties. It must be one of exactly three values:never— the damage is always the same, whatever the difficulty.always— the damage always scales with difficulty.when_caused_by_living_non_player— it scales with difficulty only if the attacker was a living entity that wasn’t a player (so a zombie’s hit scales, but your own hit doesn’t).
effects— optional. A string controlling how the hit is shown to the player: the little hurt animation and sound. One of:hurt(the default),thorns,drowning,burning,poking, orfreezing. If you leave it off, you gethurt.death_message_type— optional. A string choosing which style of death message to use. One of:default(the default),fall_variants(uses the fall-damage messages), orintentional_game_design(the joke “intentional game design” message you’ve seen from beds in the Nether). If you leave it off, you getdefault.
That’s the whole format. Notice that only three fields really must be thought about
(message_id, exhaustion, and scaling), and the game’s built-in minecraft:arrow damage type
uses exactly those three:
{
"exhaustion": 0.1,
"message_id": "arrow",
"scaling": "when_caused_by_living_non_player"
}
That single object is the entire vanilla arrow-damage definition: a tenth of a point of
exhaustion, the arrow message id, and difficulty scaling only when a non-player living thing fired
it. Your own damage types will look just as short.
Under the Hood (skippable). How does
message_idbecome a death message? Whendeath_message_typeisdefault(the standard message logic), the game builds a translation key from it. In the normal case it’sdeath.attack.<message_id>, so amessage_idofarrowlooks updeath.attack.arrow. If the killer was holding a named item, it instead usesdeath.attack.<message_id>.item, and for an “assisted” death (the dying entity was recently hurt by something living) it usesdeath.attack.<message_id>.player. You don’t have to supply those translation strings; if you don’t, the game just shows the raw key. Writing your own death-message text means adding a language file, which is a resource-pack job covered in Chapter 30; here we only need themessage_idfield itself.
Applying your damage type: the /damage command
A damage type sitting in a file does nothing until you fire it. The tool for that is /damage.
Here is its Java Edition syntax:
damage <target> <amount> [<damageType>] [at <location>]
damage <target> <amount> [<damageType>] [by <entity>] [from <cause>]
The square brackets mean “optional,” so the simplest form is just damage <target> <amount>. The
arguments:
target— which entity (or entities, via a selector) to damage.amount— how much damage to inflict.damageType— optional. Which damage type to use. If not specified, it defaults tominecraft:generic. This is where your custom damage type’s id goes.at <location>— optional. Where the damage came from, for damage that wasn’t caused by an entity (like the position of an exploding bed).by <entity>— optional. The entity that dealt the damage.from <cause>— optional. The cause of the damage, for example the skeleton that shot the arrow, as opposed to the arrow itself.
There’s one caveat worth understanding before you’re confused
in-game: the amount you type is not guaranteed to be the exact damage applied. The damage
specified by the <damageType> argument is not the exact amount of damage that
will be applied to the entity. The resulting damage is affected by statistics that would otherwise
modify or nullify it. In plain terms: your /damage runs through all the normal damage
math. If the target is wearing armor, armor still reduces it. If the type counts as fire and the
target has Fire Resistance, it’s nullified. If a creative-mode player is the target, most damage
does nothing. So /damage @s 6 minecraft:generic might land for less than 6 once armor and effects
have their say, which is exactly the behavior we’re about to exploit with a tag.
Here’s a worked example. It makes a villager named villager_1 deal 1 point of damage to
the nearest iron golem:
damage @e[type=iron_golem, sort=nearest, limit=1] 1 generic by @e[type=villager, limit=1, name="villager_1"]
Read it left to right: damage the nearest single iron golem, for 1, with the generic damage type,
dealt by the named villager. Because the villager is named the by argument, a death message
could even credit it.
Modern Minecraft. Older tutorials had no clean way to “just hurt that entity for N.” People faked it with instant-damage potion effects, or by briefly summoning harmful mobs. The
/damagecommand (and data-driven damage types behind it) is the modern, direct way: one command, an exact source and type, full control.
Damage type tags: grouping kinds of damage
You already know registry tags from Chapter 14: a JSON file under
data/<namespace>/tags/<registry>/ whose values array lists members, referenced elsewhere with a
#. A damage type tag is simply that idea applied to the damage_type registry. A damage type
tag is a group of damage types. It can be used when testing
for damage type arguments with #<resource location>, which succeeds if the damage type matches any
of the damage types specified in the tag.
So the file lives at:
data/<namespace>/tags/damage_type/<name>.json
…and you reference the whole group with a #, exactly as you referenced block and item tags in
Chapter 14. The difference is what the members are: here, each member is a damage type.
Why do these matter? Because the game uses a long list of built-in damage type tags to decide how damage behaves. There are many; here are the ones you’re most likely to care about:
bypasses_armor— “Damage from these types ignores armor reduction.” (Our void-touch trick.)bypasses_shield— “Damage from these types does not get blocked by shields.”bypasses_effects— “bypasses any damage reduction (Resistance effect and enchantments).”bypasses_resistance— bypasses the Resistance potion effect specifically.bypasses_enchantments— bypasses enchantment-based damage reduction (like Protection).is_fire— fire-type damage: ignored iffireDamageis off or the target has Fire Resistance, and reduced by Fire Protection.is_explosion— reduced by the Blast Protection enchantment.is_projectile— reduced by Projectile Protection; also used to decide if endermen teleport.is_fall— fall damage: ignored iffallDamageis off or the target has Slow Falling, and reduced by Feather Falling.witch_resistant_to— “reduce the amount of damage dealt to witches by 85%.”wither_immune_to— “Prevents the Wither from taking these damage types.”
You don’t define these tags; the game ships them. What you do is add your own damage types to
them. Want your custom damage to skip armor? Add it to bypasses_armor. Want a mob immune to it?
That’s what wither_immune_to does for the wither. Tags are the bridge between “I made a new kind of
damage” and “the game’s existing rules treat it correctly.”
Closing the loop: this is what Chapters 23 and 24 pointed at
Now we can finally settle the two promises from Part VI.
Back in Chapter 23 you built shield items with the blocks_attacks component, and two of its
fields were left waiting on this chapter. The type inside each damage_reductions rule, and the
bypassed_by field, were both described there as taking “a damage type tag.” Now you know exactly
what that means and what to write: a #-prefixed damage type tag, the kind of file you just learned
to reference. Put #minecraft:is_explosion in a damage_reductions rule’s type and that rule
blocks explosions; put a tag in bypassed_by and any incoming hit whose type is in that tag ignores
the shield entirely. Those fields were always pointing at the damage type tags this chapter defines.
Chapter 24 did the same with the damage_resistant component. Recall the fireproof cake you made
there:
/give @s cake[damage_resistant={types:"#minecraft:is_fire"}]
Its types field was described as “a damage type tag prefixed with #,” and now you can see what
that value really is: #minecraft:is_fire is the built-in is_fire damage type tag, the group of
all fire-related damage types. The cake shrugs off every member of that group at once. That’s the
whole point of tags, and the reason both components take a tag rather than a single type: one #name
stands in for a whole family of damage types, so a single field can cover “all fire damage” or “all
explosions” without listing each one.
So every “damage type tag” the earlier chapters waved at is the same thing: a data/<namespace>/tags/ damage_type/<name>.json file (or one of the built-in ones), referenced with #.
Walkthrough: the “void touch” damage type that bypasses armor
Let’s build it. The goal: a damage type called mypack:void_touch that hurts no matter what armor
the target is wearing.
Step 1 — Write the damage type
Create the file. We’ll give it a little exhaustion, no difficulty scaling, and the freezing hurt
effect so it feels eerie:
mypack/data/mypack/damage_type/void_touch.json
{
"message_id": "void_touch",
"exhaustion": 0.1,
"scaling": "never",
"effects": "freezing",
"death_message_type": "default"
}
Every field here is one we listed earlier: message_id names it (and feeds the
death.attack.void_touch translation key), exhaustion of 0.1 matches vanilla arrow damage,
scaling: "never" keeps it constant on every difficulty, effects: "freezing" borrows the icy hurt
shake, and death_message_type: "default" uses the normal message logic.
Step 2 — Make it bypass armor with a tag
On its own, void_touch is just ordinary damage that armor would soften. To make it ignore armor,
we add it to the game’s built-in bypasses_armor damage type tag. Because we’re extending a
vanilla tag, the file goes in the minecraft namespace (this is the Chapter 14 trick for adding
to a vanilla tag), and we leave replace out (or set it false) so we add to the vanilla tag
instead of wiping it:
mypack/data/minecraft/tags/damage_type/bypasses_armor.json
{
"values": [
"mypack:void_touch"
]
}
This is the same values-array tag format from Chapter 14. The only new thing is the registry
folder name, damage_type. Now mypack:void_touch is a member of bypasses_armor, and damage
from anything in that tag ignores armor reduction. Full plate or nothing, the void
touch lands the same.
Under the Hood (skippable). Why a separate tag file instead of a field on the damage type that says “ignore armor”? Because “ignore armor” isn’t a property of the damage type itself; it’s a rule the game applies to a group. Lots of different damage types might want to bypass armor; rather than repeat a flag on each, the game keeps one
bypasses_armorlist and checks membership. This is the same reasondamage_resistantandblocks_attackstake tags, not single types: one tag, many members, one rule.
Step 3 — A function to test it
Let’s summon a target and zap it. We’ll spawn a zombie (which can wear armor, making the armor-bypass visible) a few blocks in front of us, then hit it. Following our rule from Chapter 9, there’s no leading slash inside a function file:
mypack/data/mypack/function/void_touch.mcfunction
# spawn a test zombie 3 blocks in front of where the function runs
summon zombie ^ ^ ^3 {CustomName:'"Void Test"'}
# hit the nearest zombie for 6 with our custom damage type
damage @e[type=zombie, sort=nearest, limit=1] 6 mypack:void_touch
say Void touch applied!
Save everything, then in-game:
/reload
/function mypack:void_touch
A zombie named “Void Test” appears ahead of you and immediately takes 6 points of void-touch damage
with the icy freezing flash. Because void_touch is in the bypasses_armor tag, that 6 lands in
full even if the zombie spawns wearing armor. Try it next to a normal hit and you’ll see armored
mobs take the void touch just as hard as bare ones.
Modern Minecraft. This whole flow (define a kind of damage in a file, group it with a tag, apply it with one command) didn’t exist in older versions, where damage types were hardcoded. Being able to ship a brand-new damage type in a data pack, and slot it into the game’s existing armor/shield/resistance rules just by adding it to the right tag, is a genuinely modern capability.
Figure (to be captured). a player running /function mypack:void_touch; a zombie named “Void Test” ahead taking the freezing hurt flash, with the “Void touch applied!” chat line
Practice
-
A gentler touch. Make a second damage type,
mypack:soft_touch, that’s identical but does not go inbypasses_armor. Find or spawn an armored mob (zombies sometimes spawn wearing armor; or test on yourself wearing armor in Survival), hit it with both, and watch the armored target take less fromsoft_touchthan fromvoid_touch. This shows the tag (not the damage type file) is what bypasses armor. -
Borrow a vanilla feeling. Change your
void_touchfile’seffectsfield toburninginstead offreezing,/reload, and run the function again. Same damage, different on-screen reaction. Try each of the legal values (hurt,thorns,drowning,burning,poking,freezing) and notice thateffectsonly changes the look and sound, not the numbers. -
Scaling test. Make a
mypack:hard_hitdamage type with"scaling": "always". Apply it with/damageon Easy, then switch your world to Hard and apply it again with the sameamount. Because it scales with difficulty, the harder setting should hurt more. (Set difficulty from the game menu or with the difficulty command.) -
Make a shield ignore it. Revisit your Chapter 23 shield item. Build a
blocks_attacksitem whosebypassed_byfield is a#-prefixed damage type tag that containsmypack:void_touch(you can make your own tag file, e.g.mypack:voidish, withvoid_touchin itsvalues). Block with the item while a function hits you withvoid_touch, and confirm the block does nothing, because the tag is inbypassed_by. You’ve now wired your Chapter 23 component to your Chapter 36 tag with no missing pieces.
What Can Go Wrong
What Went Wrong? My
/damage“worked” but the target barely lost health. Remember the caveat: theamountyou type is not the final damage; it runs through normal reductions. Armor, Resistance, Protection enchantments, and Fire Resistance can all soften or cancel it. If you want it to ignore one of those, add your damage type to the matching built-in tag (bypasses_armor,bypasses_resistance,bypasses_enchantments, and so on). And note that a creative-mode target is immune to most damage entirely. Test in Survival.
What Went Wrong? The game says my damage type is an unknown damage type. Two usual causes. First, the file path must be exactly
data/<namespace>/damage_type/<name>.json(singulardamage_type, just like the singularfunction,recipe, andloot_tablefolders from earlier chapters). Second, you must reference it by its full id in/damage:mypack:void_touch, not justvoid_touch. After fixing either, run/reloadand try again.
What Went Wrong? I added my type to
bypasses_armorbut it wiped out all the vanilla armor-bypassing damage. You almost certainly put"replace": truein the tag file, or you put the tag in your own namespace instead ofminecraft. To extend the vanillabypasses_armortag, the file must be atdata/minecraft/tags/damage_type/bypasses_armor.jsonand must leavereplaceout (it defaults tofalse). That merges your type into the existing list, the extend-vs-replace rule you learned in Chapter 14.