Events
Register with:
Game.registerEvent(Game.Events.TEXT_MESSAGE, function(ev)
-- handle
end)
Multiple listeners per event are supported. Game.unregisterEvent(ev, fn) removes one listener; Game.unregisterEvent(ev) clears all for that event.
Callbacks run on the Lua background thread. Do not block for long; heavy work should be short or deferred via timers.
Wired events
TEXT_MESSAGE
Server / client text messages (status, advance, loot, …).
| Field | Type | Notes |
|---|---|---|
text | string | message body |
channelId | number | often 0 for system |
messageType | number | see Enums.MessageTypes (version-sensitive) |
Game.registerEvent(Game.Events.TEXT_MESSAGE, function(ev)
if ev.text == "You are dead."
and (ev.channelId or 0) == 0 then
Client.sendHotkey(16777220, 0)
end
end)
TALK
Chat / creature talk.
| Field | Type |
|---|---|
| talker / name | in payload string fields |
text | message |
(Exact field names follow the native marshal — prefer print / log once when integrating.)
LABEL
Status-line / label text (client status line updates).
| Field | Type |
|---|---|
text | string |
CONTAINER_OPEN / CONTAINER_CLOSE
| Field | Type | Notes |
|---|---|---|
containerId | number | open / close |
capacity | number | open only |
CONTAINER_CLOSE fires from the container metacall (handleCloseContainerMessage, id @ msg+0x18) after the client applies the close.
MAGIC_EFFECT / DISTANCE_SHOOT_EFFECT
Opt-in via hook_magic_effects=1 (default off). Resolve is optional: unique AOB miss ⇒ events stay off (no crash). Install re-checks prologue + stolen size.
Offline-verified offers: frame-1d8 (15.11 / 15.13), frame-208 (15.23 / 15.25 / gunzodus-15.25.3a).
| Event | Fields |
|---|---|
MAGIC_EFFECT | type, optional x,y,z (coords when Any parse + Coordinate* validate; else type-only) |
DISTANCE_SHOOT_EFFECT | type, from x,y,z, to toX,toY,toZ (missile only when to Coordinate* is plausible — GraphicalEffect delay at the same offset is ignored) |
CREATURE_APPEAR
| Field | Type |
|---|---|
| creature id | number |
| x, y, z | position |
| name | string |
LEVEL_UP
| Field | Type |
|---|---|
| old / new level | numbers |
PLAYER_STAT_CHANGE
| Field | Type |
|---|---|
| stat id | hp / mana / exp / vocation codes |
| new value | number |
MODAL_WINDOW
Modal dialog interaction payload (button / choice ids).
IMBUEMENT_OPEN_WINDOW / IMBUEMENT_DATA
Fired when the client receives GameserverMessageImbuingDialogRefresh (shrine / imbuement dialog open or refresh).
IMBUEMENT_DATA payload (ZeroBot-compatible):
| Field | Type | Notes |
|---|---|---|
itemId | number | item currently in the imbuement window |
slots | number | imbuement slot capacity on the item |
clearingCharmCost | number | optional; present when packet flag bit 2 is set |
slotImbuements | array | current slot contents (may include empty=true entries) |
availableImbuements | array | imbuements that can be applied |
Each entry in slotImbuements / availableImbuements:
| Field | Type |
|---|---|
empty | bool |
imbuementId | number |
imbuementLevel | string |
imbuementName | string |
imbuementDescription | string |
imbuementPrice | number |
clearPrice | number |
timeRemaining | number |
items | array of { itemId, count } |
Remaining RE: astralSources (player materials on hand at msg+0x50/0x58).
QUEST_LOG
Fires when the client applies a quest-log update (after the server
GameserverMessageQuestLog is parsed into TQuestLogStorage).
Callback receives one table (ZeroBot onInternalQuestLog shape):
Game.registerEvent(Game.Events.QUEST_LOG, function(data)
for _, q in ipairs(data.quests) do
print(q.id, q.name, q.state) -- state: Enums.QuestState PENDING=0 COMPLETED=1
end
end)
| Field | Type |
|---|---|
quests | array of { id, name, state } |
QUEST_LINES
Fires when quest-line / mission details are applied for a quest id.
ZeroBot positional fan-out: (questId, { missions = {...} }).
Game.registerEvent(Game.Events.QUEST_LINES, function(questId, missions)
for _, m in ipairs(missions.missions) do
print(m.missionId, m.name, m.description)
end
end)
| Field (table form) | Type |
|---|---|
questId | number |
missions | array of { missionId, name, description } |
PARTY_HUNT
Fires on handlePartyHuntAnalyserMessage (party hunt analyser RX).
ZeroBot fan-out passes the formatted output string (Engine tab party-hunt
calculator scrapes Balance: from it). Opus also fills structured fields on
the native event table.
Game.registerEvent(Game.Events.PARTY_HUNT, function(output)
print(output)
end)
| Field (table form) | Type |
|---|---|
output | string |
sessionStart | number |
partyLeaderId | number |
lootType | number |
members | array of { id, name, highlight, loot, supply, damage, healing } |
Tick vs events
| Mechanism | Use when |
|---|---|
function onTick() | polling vitals, HUD refresh, light logic every tick |
Timer.new | fixed-interval work (2s bless check, 500ms blink) |
Game.registerEvent | react to server/client messages without polling |
Prefer events for “when X happens” and timers for “every N ms”. Avoid busy loops.