Repository navigation
API Entity Health and Loot
Lua API / Entities / Entity health and loot
Entity health, damage callbacks, death transitions, and loot are separate but coordinated systems. Declaring health does not derive collision or appearance from the model.
Living entities use living.maxHealth. Props, projectiles, and pickups can opt into destructible health with health = { max = 12 }, where max is an integer from 1 to 32767. Their current health is saved and cannot exceed the declared maximum after a reload. Without this field, these kinds retain their normal native hit behavior. They can still call entity:kill() from a hit or interaction callback to enter the death lifecycle. entity:getHealth(), :getMaxHealth(), and :heal(amount) work with either kind of health.
onBeforeDamage(ctx) receives entity, world, amount, optional health, attacker, and directSource, plus a best-known cause. Return nil to keep the amount, 0 to cancel, or an integer from 1 to 32767 to replace it. It runs before native armor, knockback, death, and drops. onAfterDamage(ctx) runs after damage handling and receives requestedAmount, the submitted amount, accepted, and, when health is available, healthBefore, healthAfter, and healthLost. The two callbacks fail independently: an error in onBeforeDamage cancels that hit and disables that callback; an error in onAfterDamage disables only that callback.
cause is "explosion" for BetaMoon-created explosions, "projectile" for BetaMoon projectile hits, "script" for handle-based attacks, and otherwise "entity" or "environment" based on whether a native attacker is known. Explosion damage also supplies ctx.explosion, a detached request snapshot. attacker is the entity credited with damage when known; directSource is the immediate source when known. For a BetaMoon projectile, the owner is the attacker and the projectile is the direct source. accepted can be true even when armor or native invulnerability prevents health loss; inspect healthLost when that distinction matters. Vanilla does not supply a precise cause for every hit.
health = { max = 12 },
onBeforeDamage = function(ctx)
if ctx.cause == "projectile" then
return 0 -- Ignore BetaMoon projectile hits.
end
return nil
end,
onDeath = function(ctx)
-- Award loot or trigger another scripted effect.
endFor fatal hits, onDeath runs before onAfterDamage; onDeactivate and onRemove follow when the entity is marked dead. A living entity with native lifecycle can remain briefly as a death corpse before removal. With manual lifecycle, Lua must call entity:remove() to finish removal after death; onTick continues so it can do so, while AI routines stop. Calling kill() inside onBeforeDamage completes the death transition and stops the remaining damage pipeline, including onAfterDamage. An entity with zero health cannot be healed or revived. Repeated fatal calls do not replay death callbacks or death drops. Damage callbacks cannot start another damage call on the same entity while handling a hit.
drops accepts legacy shorthand entries, one registered loot table, or inline loot pools. It is
sampled when an entity enters its death lifecycle or Lua calls entity:dropLoot(). Every death or explicit call uses
a fresh sample from the world's runtime RNG. Explicit remove() calls, projectile expiry, pickup collection, and
despawning do not produce loot by themselves.
drops = {
{ item = 5035, min = 1, max = 1 },
{ item = betamoon.items:getRequired(331), min = 0, max = 2, chance = 0.4 }
}
-- Alternatively:
drops = { table = "mymod:common_drops" }
-- Or use the loot-table pool syntax directly:
drops = {
pools = {
{
key = "main",
rolls = { min = 1, max = 2 },
entries = {
{ type = "item", weight = 3, stack = { item = 264 } },
{ type = "empty", weight = 1 }
}
}
}
}For the legacy list, each entry requires item as an item reference or registered item/block stack ID. min and
max describe the total item count, default to 1, and accept 0–64. BetaMoon splits that total into legal native
stacks, so a result of three nonstackable tools creates three dropped stacks of one. chance defaults to 1 and
accepts 0–1, and damage defaults to 0.
onBeforeDamage = function(ctx)
if ctx.attacker and ctx.attacker:isPlayer() then
ctx.entity:kill(ctx.attacker)
end
return 0
endSaved inventory and logical equipment use their own dropOnDeath = true options. Those contents and declared drops are emitted before onDeath. The death lifecycle runs this logic once. Each explicit dropLoot() call runs it again, resampling declared drops and dropping and clearing any eligible contents currently present. This permits removal with loot without reporting a death:
ctx.entity:dropLoot()
ctx.entity:remove()Use entity:dropItem(item) to emit one specific stack without consulting the declaration. It accepts an item reference, numeric item ID, or { id, count, damage } table. These methods create native dropped-item entities; no separate pickup entity type is required.
- Installation
- Install scripts
- Getting started
- VS Code and LuaLS
- Examples
- Reload and compatibility
- Troubleshooting
- Changelog
Scripts and shared values
- Script lifecycle
- Packaged Lua mods
- Modules
- References and stacks
- Queries
- Overrides
- Minecraft constants
- Chat and positions
- Structured data
- Script configuration
- Particles
Blocks and items
- Multiblocks
- Blocks
- Block state and placement
- Block shapes and rendering
- Block callbacks
- Block ticks and redstone
- Items
- Fluids
- Item callbacks
- Tools and armor
- Materials
- Textures
Entities
- Entities
- Entity access and movement
- Entity lifecycle and data
- Entity health and loot
- Entity inventories and relationships
- Projectiles and pickups
- Entity presentation and sounds
- Living entities and AI
- Entity parts and sensors
Assets and audio
- Assets
- Models and animations
- Built-in Minecraft models
- Model appearances
- Blockbench workflow
- Audio
- Texture-pack overrides
Events and world actions
World generation
World systems
Inventories and screens
Recipes