Skip to main content
Signals are the primary way for scripts to talk to each other. When one entity needs to notify another — a pressure plate that opens a door, a trigger block that wakes up a sleeping enemy — you emit a signal by name, and every entity whose OnSignal hook matches that name can respond. Signals can carry any Lua value as a payload, so you can pass numbers, strings, tables, or even nothing at all.

Receiving a Signal: The OnSignal Hook

To make an entity respond to a signal, define OnSignal in its script. The game calls this function whenever a signal is delivered to that entity.
---@param self Block
---@param signal Signal
function OnSignal(self, signal)
    if signal.id == "Activate" then
        self:enable()
    elseif signal.id == "Deactivate" then
        self:disable()
    end
end

The Signal Object

The signal parameter gives you everything you need to act on the incoming message:
PropertyTypeDescription
signal.idstringThe name of the signal — use this to tell signals apart.
signal.dataanyOptional payload; can be a number, string, table, or nil.
signal.targetElementThe specific entity this signal was addressed to (if any).
signal.tagFilterstringThe tag filter used when emitting (if any).
signal.typeFilterEntityTypeThe entity-type filter used when emitting.
signal.customFilterfunctionThe custom filter function used when emitting (if any).
You can create a Signal object manually if you need full control over filtering:
local sig = Signal.new{
    id         = "OpenDoor",
    data       = { doorId = "north-gate" },
    tagFilter  = "doors",
    typeFilter = EntityType.Block,
}
Signals.emit(sig)

Emitting Signals: The Signals Namespace

The Signals namespace provides several functions that let you target signals precisely — from a broadcast to every entity in the level down to a single named entity.

Signals.emit(signal)

Emits a pre-built Signal object, respecting whatever filters are set on it.
--- @param signal Signal
Signals.emit(signal)

--- @param signalProperties table
Signals.emit(signalProperties)  -- shorthand table form
Use the table shorthand when you want a quick one-liner:
Signals.emit{ id = "Explode", data = { radius = 3 } }

Signals.emitToAll(signalId [, data])

Broadcasts a signal to every entity in the level.
--- @param signalId string
--- @param data any   -- optional
Signals.emitToAll(signalId, data)
-- Wake up everything
Signals.emitToAll("WakeUp")

-- Send a damage value to all entities
Signals.emitToAll("TakeDamage", { amount = 10 })

Signals.emitTo(signalId, target [, data])

Sends a signal to one specific entity.
--- @param signalId string
--- @param target Element
--- @param data any        -- optional
Signals.emitTo(signalId, target, data)
-- Tell a specific block to open
local door = Elements.tagged["north-door"]:asBlock()
Signals.emitTo("Open", door)

Signals.emitToTag(signalId, tag [, data])

Sends a signal to all entities that share a given tag.
--- @param signalId string
--- @param tag string
--- @param data any   -- optional
Signals.emitToTag(signalId, tag, data)
-- Toggle every entity tagged "light-fixture"
Signals.emitToTag("Toggle", "light-fixture")

Signals.emitToType(signalId, type [, data])

Sends a signal to all entities of a specific EntityType.
--- @param signalId string
--- @param type EntityType
--- @param data any          -- optional
Signals.emitToType(signalId, type, data)
-- Freeze every enemy in the level
Signals.emitToType("Freeze", EntityType.Enemy)

Signals.emitToTypeAndTag(signalId, type, tag [, data])

Sends a signal to all entities that match both a type and a tag — the most precise broadcast filter.
--- @param signalId string
--- @param type EntityType
--- @param tag string
--- @param data any          -- optional
Signals.emitToTypeAndTag(signalId, type, tag, data)
-- Only target blocks tagged "trap"
Signals.emitToTypeAndTag("Arm", EntityType.Block, "trap")

Signals.emitToCustom(signalId, customFilter [, data])

Sends a signal to every entity for which customFilter returns true. The filter receives the entity and the signal as arguments.
--- @param signalId string
--- @param customFilter function
--- @param data any              -- optional
Signals.emitToCustom(signalId, customFilter, data)
-- Only signal blocks that are currently moving
Signals.emitToCustom("Stop", function(entity, signal)
    if entity.type == EntityType.Block then
        return entity:asBlock().isMoving
    end
    return false
end)

Signal Data

The data field accepts any Lua value — a plain boolean, a number, a string, or a full table with nested fields.
-- Sending structured data
Signals.emitToTag("Configure", "spawners", {
    count    = 3,
    interval = 2.0,
    active   = true,
})
On the receiving side, read it back from signal.data:
function OnSignal(self, signal)
    if signal.id == "Configure" then
        local cfg = signal.data
        print("Spawning " .. cfg.count .. " enemies every " .. cfg.interval .. "s")
    end
end
If you emit a signal without a data argument, signal.data will be nil on the receiving end. Always guard with a nil check if the field is optional.

Full Example: Pressure Plate and Gate

The following two scripts work together. A Side script acts as a pressure plate: when the ball rolls on, it emits a signal. A Block script on a gate block listens for that signal and toggles itself.
-- Side script attached to the pressure-plate tile
-- Emits "PressurePlate_On" when the ball rolls on and
-- "PressurePlate_Off" when it rolls off.

---@param self Side
---@param rollIn boolean
---@param ball Ball
function OnBallRoll(self, rollIn, ball)
    if rollIn then
        Signals.emitToTag("PressurePlate_On", "gate-block")
    else
        Signals.emitToTag("PressurePlate_Off", "gate-block")
    end
end
Use tag-based emission (Signals.emitToTag) when you want to control multiple entities at once without keeping references to each one individually.