Skip to content

API Entity Parts and Sensors

Piggo41 edited this page Sep 28, 2026 · 1 revision

Entity parts and sensors

Lua API / Entities / Entity parts and sensors

Multipart shapes and sensors add server-side gameplay regions around an entity. They are independent of rendered model geometry and animation.

Body, multipart shapes, and sensors

Multipart damage and interaction shapes

parts names bones in the model. A part with no gameplay shape remains visual only. hitbox is its damage shape; interactionBox can provide a separate right-click shape. Both use offset = { x, y, z } for the center relative to the entity base and positive size = { x, y, z }, in blocks. If interactionBox is omitted, an existing hitbox also handles interaction for compatibility.

onDamage(ctx) requires hitbox. It receives ctx.amount and optional ctx.attacker; return a new integer 0–32767 to change damage, or nil to keep it. onInteract(ctx) requires either shape and uses the normal pass/handled/deny results. Both contexts provide the part name in ctx.part, the parent in ctx.entity, and the selected proxy in ctx.hitbox. Call ctx.hitbox:getPartRole() to distinguish "hit" from "interaction" when needed.

If a part callback errors, BetaMoon cancels that interaction or hit and disables only the failing callback for the current declaration. Later interactions pass to the parent, and later hits use the original damage amount. Removing the parent during a part callback stops further parent handling for that event.

parts = {
    head = {
        hitbox = {
            offset = { x = 0, y = 1.5, z = 0 },
            size = { x = 0.5, y = 0.5, z = 0.5 }
        },
        interactionBox = {
            offset = { x = 0, y = 1.5, z = 0 },
            size = { x = 0.8, y = 0.8, z = 0.8 }
        },
        onDamage = function(ctx)
            return ctx.amount * 2
        end
    },
    tail = {} -- Visual only.
}

entity:setPartOffset(name, x, y, z) moves a damage shape. entity:setInteractionOffset(name, x, y, z) moves an interaction shape. Each returns false if that shape is absent. These offsets are runtime state, so recalculate them after load from saved data when needed.

Gameplay shapes do not automatically follow animated bones. Move them from server-side callbacks when a changing pose affects gameplay; use the appearance's onPose or an animation clip for visuals. Shapes remain axis-aligned: entity yaw rotates their center offset, but does not rotate the box itself. Visual model rotation and scale never change gameplay collision.

Overlap sensors

sensors defines up to 16 named, non-colliding server-side areas. Each requires a shape and at least one of onEnter, onStay, or onLeave. filter accepts "all" (default), "players", or "living". intervalTicks controls scan frequency from 1 to 1200 ticks and defaults to 5. A scan tracks at most 64 occupants per sensor.

sensors = {
    nearby_player = {
        shape = {
            offset = { x = 0, y = 1, z = 0 },
            size = { x = 8, y = 3, z = 8 }
        },
        filter = "players",
        intervalTicks = 5,
        onEnter = function(ctx)
            ctx.entity:faceToward(ctx.other, 20, 20)
        end,
        onLeave = function(ctx)
            -- ctx.reason explains whether it left, was removed, or unloaded.
        end
    }
}

Sensor contexts contain entity, other, sensor, and world. onLeave also receives reason: "left", "removed", or "unloaded" for an occupant transition, or the owner's deactivation/removal reason such as "chunk_unload", "simulation_paused", "definition_missing", "definition_changed", "explicit", or "death". entity:setSensorOffset(name, x, y, z) moves a sensor center and returns false when the sensor does not exist. Sensor offsets are transient and follow the same reload and rotation rules as part offsets.

Clone this wiki locally