Hungrit Docs

Damage and destruction#

Things that can be broken, and things that break them.

Making something breakable#

An object is damageable only if it has a max_health.

Luau
self:set({ max_health = 100, health = 100 })

Without it, damage and heal refuse with this object has no max_health. That is how a world says which props are breakable and which are scenery, otherwise every prop could be deleted by any script that could reach it.

Hurting itself#

Luau
--!strictlocal self = require("self") self:set({ max_health = 60 }) self:on_touch(function(p)    local left = self:damage(20, { by = p.id })    if not left then return end     if left <= 0 then        print("felled")    else        self:set({ transparency = 1 - (left / 60) })    endend)

by credits somebody, for a scoreboard or a kill feed.

Tip

The number you get back is a prediction. The subtraction happens on the server, because two axes hitting the same tree on the same tick would otherwise both read the same health and the tree would take one hit for two swings.

What you receive is what your blow would leave, which is exactly what you need to decide whether you just felled it. For the authoritative outcome, listen for world.destroyed.

Hurting something else#

Luau
local world = require("world")local objects = require("objects") local hit = world:raycast({ from = shot.eye, dir = shot.dir, ignore_player = p.id }) if hit and hit.what == "object" then    local obj = objects:get(hit.object)    if obj then        obj:damage(25, { by = p.id })    endend

objects:damage answers true, not the remaining health, because no script holds a mirror of another object's life.

Reacting to a destruction#

Luau
--!strictlocal events = require("events")local world = require("world") events:listen("world.destroyed", function(e)    if type(e) ~= "table" then return end     world:spawn({        shape = "sphere",        pos = { x = e.x, y = e.y, z = e.z },        scale = 0.4,        color = "#8c5a2b",        collide = false,        life = 4,    })end)

This is the authoritative event. Use it for the loot, the score and the sound, not the prediction from damage.

Careful

world.destroyed is not world.collapse. Destroyed is about damage, meaning health reached zero. Collapse is about support: a building piece lost what was holding it up and came down. A wall can do either, and they usually deserve different reactions.

A tree that drops something#

Luau
--!strictlocal self = require("self")local world = require("world")local players = require("players")local events = require("events") self:set({ max_health = 80 }) self:on_action("chop", { key = "e" }, function(p)    local who = players:get(p.id)    if not who then return end    if self:position():dist(who.pos) > 3 then return end     local left = self:damage(20, { by = p.id })    if not left then return end     self:play_sound("chop", { pitch = { 0.9, 1.1 }, range = 30 })     if left > 0 then        return    end     local here = self:position()    for i = 1, 3 do        world:spawn({            model = "log",            pos = { x = here.x + i * 0.4, y = here.y + 1, z = here.z },            life = 120,        })    endend)

Three spawns plus a damage plus a sound is five effects, comfortably inside the eight-per-event ceiling. Ten logs would not be.

Player health#

Players do not have health the way objects do. What a hit costs is your game's rule. Keep it in one place:

Luau
--!strictlocal self = require("self")local players = require("players")local events = require("events") local SPAWN = { x = 0, y = 1, z = 0 }local MAX = 100 local hp = {} players:on_enter(function(p) hp[p.id] = MAX end)players:on_leave(function(p) hp[p.id] = nil end) local function show(id: string)    local who = players:get(id)    if who then        who:update_hud("vitals", { hp = hp[id] or 0 })    endend events:listen("combat:hit", function(e)    if type(e) ~= "table" then return end     local id = tostring(e.target)    local left = (hp[id] or MAX) - (tonumber(e.amount) or 0)     if left > 0 then        hp[id] = left        show(id)        return    end     hp[id] = MAX    show(id)     local ok, why = players:teleport(id, SPAWN)    if not ok then        print("could not respawn: " .. why)    end     events:emit("combat:down", { who = id, by = e.by })end)

Check what teleport answers. Moving somebody else needs Act on other people's avatars, which the world's owner holds and may grant. The symptom of not checking is not a crash. It is a respawn that silently never happens.

Regenerating#

Luau
self:on_timer(2, function()    for id, v in pairs(hp) do        if v < MAX then            hp[id] = math.min(MAX, v + 3)            show(id)        end    endend)

Mending an object#

Luau
self:heal(15)

Same rules and the same prediction as damage.

See also#

Updated 11 October 2026