Events
Events allow handling incoming game data (packets, timers, messages) in a decentralized way — independently from the main script logic.
For example: while your crafting macro runs, an anti-macro gump may appear. Without events, you would need to check for the gump at every step. With events, the gump is handled by a separate handler function automatically.
Another common use: monitoring connection state, reacting to incoming speech, tracking buff/debuff changes — all without polluting the main script loop with checks.
События позволяют обрабатывать входящие игровые данные (пакеты, таймеры, сообщения) децентрализованно — независимо от основной логики скрипта.
Например: пока работает макрос крафта, может появиться антимакро-гамп. Без событий пришлось бы проверять наличие гампа на каждом шаге. С событиями гамп обрабатывается отдельной функцией автоматически.
Другие типичные применения: мониторинг состояния подключения, реакция на входящую речь, отслеживание бафов/дебафов — всё это без засорения основного цикла проверками.
Event handlers are not executed in parallel. They are called from within the Wait method (and WaitForEvent in Python). When an event fires, it is added to an internal queue and executed in the script one by one during the next Wait call.
Important rules:
- Never put heavy operations in event handlers: no movement (MoveXY), no waiting (WaitForTarget), no long loops. Only lightweight and fast code.
- Events are available for all internal (Pascal) and external (Python) scripts. They work the same everywhere.
- Events triggered by network packets require an active connection. No connection → no packets → no packet-based events. Timer events (evTimer1, evTimer2) are internal and do not depend on packets.
Обработчики событий выполняются не параллельно. Они вызываются изнутри метода Wait (и WaitForEvent в Python). При наступлении события оно добавляется во внутреннюю очередь и выполняется в скрипте по одному во время следующего вызова Wait.
Важные правила:
- Никогда не размещайте тяжёлые операции в обработчиках: никакого перемещения (MoveXY), ожидания (WaitForTarget), длинных циклов. Только лёгкий и быстрый код.
- События доступны для всех внутренних (Pascal) и внешних (Python) скриптов. Работают одинаково везде.
- События от сетевых пакетов требуют активного подключения. Нет подключения → нет пакетов → нет пакетных событий. Таймерные события (evTimer1, evTimer2) — внутренние и не зависят от пакетов.
Register a handler with SetEventProc:
SetEventProc(evSpeech, 'OnSpeech');
Unregister by passing an empty string:
SetEventProc(evSpeech, '');
If a handler is already registered for an event and you set a different one without clearing first, a warning is logged. Always unregister the old handler before setting a new one.
Зарегистрируйте обработчик через SetEventProc:
SetEventProc(evSpeech, 'OnSpeech');
Снятие обработчика — передайте пустую строку:
SetEventProc(evSpeech, '');
Если обработчик уже назначен и вы устанавливаете другой без предварительного снятия — выводится предупреждение. Всегда снимайте старый обработчик перед установкой нового.
Python offers two modes for working with events.
В Python есть два режима работы с событиями.
Register a handler function via SetEventProc. The handler is called automatically during Wait.
Зарегистрируйте функцию-обработчик через SetEventProc. Обработчик вызывается автоматически во время Wait.
def speech_handler(text, sender_name, sender_id):
AddToSystemJournal(f'Speech {sender_name}({sender_id}): {text}')
SetEventProc(EventType.EvSpeech, speech_handler)
while True:
Wait(100)
Unregister by calling without callback:
SetEventProc(EventType.EvSpeech)
If the callback accepts fewer parameters than the event provides, extra arguments are silently truncated. For example, a handler def on_speech(text): will work for evSpeech — only the first argument (text) will be passed.
Если callback принимает меньше параметров, чем предоставляет событие, лишние аргументы отбрасываются. Например, обработчик def on_speech(text): будет работать для evSpeech — передастся только первый аргумент (text).
Subscribe to events via SetEventCallback, then poll with WaitForEvent. No callback function needed.
Подпишитесь на события через SetEventCallback, затем опрашивайте через WaitForEvent. Callback-функция не нужна.
SetEventCallback(EventType.EvSpeech)
SetEventCallback(EventType.EvItemInfo)
while True:
event = WaitForEvent(100)
if event:
print(event)
WaitForEvent(timeout_ms) returns a StealthEvent object or None on timeout. If timeout_ms is 0 or omitted — waits indefinitely.
WaitForEvent(timeout_ms) возвращает объект StealthEvent или None при таймауте. Если timeout_ms равен 0 или не указан — ожидание бесконечное.
Unsubscribe:
ClearEventCallback(EventType.EvSpeech)
SetEventProc accepts event type as:
EventTypeenum:EventType.EvSpeech- String:
'EvSpeech'(case-insensitive) - Integer:
2
SetEventCallback / ClearEventCallback accept EventType enum or integer only.
SetEventProc принимает тип события как:
- Перечисление
EventType:EventType.EvSpeech - Строку:
'EvSpeech'(регистронезависимо) - Целое число:
2
SetEventCallback / ClearEventCallback принимают только перечисление EventType или целое число.
WaitForEvent returns typed dataclass objects for many event types. Each has an id field (EventType), an arguments list (raw values), and named fields for convenience:
WaitForEvent возвращает типизированные dataclass-объекты для многих типов событий. Каждый содержит поле id (EventType), список arguments (сырые значения) и именованные поля для удобства:
@dataclass
class StealthEvent: # base: id, arguments[]
class SpeechEvent: # text, sender_name, serial
class ItemInfoEvent: # serial
class ItemDeletedEvent: # serial
class DrawObjectEvent: # serial
class DrawContainerEvent: # serial, graphic_id
class AddItemToContainerEvent: # serial, container_serial
class AddMultipleItemsInContEvent: # container_serial
class RejectMoveItemEvent: # reason
class IncomingGumpEvent: # serial, gump_id, x, y
class MenuEvent: # serial, menu_id
class ContextMenuEvent: # serial
class SoundEvent: # sound_id, x, y, z
class CharAnimationEvent: # serial, action
class MoveRejectionEvent: # x_src, y_src, direction, x_dst, y_dst
Events without a typed subclass are returned as base StealthEvent with raw values in arguments[].
События без типизированного подкласса возвращаются как базовый StealthEvent с сырыми значениями в arguments[].
Incoming item (not a mobile).
Входящий предмет (не мобайл).
Pascal: procedure OnItemInfo(ID: Cardinal);
Python: def on_item_info(id: int)
→ ItemInfoEvent(serial)
Item deleted from the world.
Предмет удалён из мира.
Pascal: procedure OnItemDeleted(ID: Cardinal);
Python: def on_item_deleted(id: int)
→ ItemDeletedEvent(serial)
Incoming speech message (includes regular speech, party messages prefixed with PartyPrivateMsg: / PartyChatMsg:, and Unicode speech).
Входящее речевое сообщение (включая обычную речь, сообщения группы с префиксом PartyPrivateMsg: / PartyChatMsg:, и Unicode-речь).
Pascal: procedure OnSpeech(Text: String; SenderName: String; SenderID: Cardinal);
Python: def on_speech(text: str, sender_name: str, sender_id: int)
→ SpeechEvent(text, sender_name, serial)
Server rejected the movement step.
Сервер отклонил шаг перемещения.
Pascal: procedure OnMoveRejection(Xorig, Yorig: Word; Dir: Byte; X, Y: Word);
Python: def on_move_rejection(x_orig: int, y_orig: int, dir: int, x: int, y: int)
→ MoveRejectionEvent(x_src, y_src, direction, x_dst, y_dst)
Container being opened/drawn.
Контейнер открывается/отрисовывается.
Pascal: procedure OnDrawContainer(ID: Cardinal; ModelGump: Word);
Python: def on_draw_container(id: int, model_gump: int)
→ DrawContainerEvent(serial, graphic_id)
Item added to a container.
Предмет добавлен в контейнер.
Pascal: procedure OnAddItemToContainer(ObjID: Cardinal; ContainerID: Cardinal);
Python: def on_add_item(obj_id: int, container_id: int)
→ AddItemToContainerEvent(serial, container_serial)
Multiple items added to a container (packet 0x3C).
Несколько предметов добавлены в контейнер (пакет 0x3C).
Pascal: procedure OnAddMultipleItems(ContainerID: Cardinal);
Python: def on_add_multiple(container_id: int)
→ AddMultipleItemsInContEvent(container_serial)
Drag/drop operation rejected by the server.
Операция перетаскивания отклонена сервером.
Pascal: procedure OnRejectMoveItem(Reason: Byte);
Python: def on_reject_move_item(reason: int)
→ RejectMoveItemEvent(reason)
Any mobile (character or NPC) drawn/updated.
Любой мобайл (персонаж или NPC) отрисован/обновлён.
Pascal: procedure OnDrawObject(ID: Cardinal);
Python: def on_draw_object(id: int)
→ DrawObjectEvent(serial)
A menu appeared.
Появилось меню.
Pascal: procedure OnMenu(Serial: Cardinal; ID: Word; Name: String);
Python: def on_menu(serial: int, id: int, name: str)
→ MenuEvent(serial, menu_id)
Note: Pascal handler receives 3 parameters. The Name field contains the menu title.
Обработчик в Pascal получает 3 параметра. Поле Name содержит заголовок меню.
Map/treasure message received.
Получено сообщение о карте/сокровище.
Pascal: procedure OnMapMessage(ID: Cardinal; CenterX, CenterY: Word);
Python: def on_map_message(id: int, center_x: int, center_y: int)
→ StealthEvent (no typed subclass)
Attack permission response.
Ответ на разрешение атаки.
Pascal: procedure OnAllowRefuseAttack(ID: Cardinal; AttackAllowed: Boolean);
Python: def on_allow_refuse(id: int, attack_allowed: bool)
→ StealthEvent
ID is 0 if AttackAllowed is False.
ID равен 0, если AttackAllowed = False.
Cliloc (localized) speech message.
Клилок-сообщение (локализованная речь).
Pascal: procedure OnClilocSpeech(SenderID: Cardinal; SenderName: String;
ClilocID: Cardinal; Text: String);
Python: def on_cliloc(sender_id: int, sender_name: str, cliloc_id: int, text: str)
→ StealthEvent
A buff or debuff was activated/deactivated on the player.
Баф или дебаф активирован/деактивирован на игроке.
Pascal: procedure OnBuffDebuff(PlayerID: Cardinal; IconID: Word; IsEnabled: Boolean);
Python: def on_buff(player_id: int, icon_id: int, is_enabled: bool)
→ StealthEvent
Stealth (or the client through it) sent a resync request. No parameters.
Stealth (или клиент через него) отправил запрос ресинхронизации. Без параметров.
Pascal: procedure OnResync;
Python: def on_resync()
→ StealthEvent
Character animation played.
Анимация персонажа воспроизведена.
Pascal: procedure OnCharAnimation(ID: Cardinal; Action: Word);
Python: def on_animation(id: int, action: int)
→ CharAnimationEvent(serial, action)
A gump was received from the server.
Гамп получен от сервера.
Pascal: procedure OnIncomingGump(Serial: Cardinal; GumpID: Cardinal; X, Y: Integer);
Python: def on_gump(serial: int, gump_id: int, x: int, y: int)
→ IncomingGumpEvent(serial, gump_id, x, y)
Internal timer, fires every ~100ms. No parameters.
Not called while the script is paused (including pause-on-disconnect). Events accumulate in the queue during the 100ms interval; they are consumed when the script calls Wait.
Внутренний таймер, срабатывает каждые ~100мс. Без параметров.
Не вызывается, пока скрипт на паузе (включая паузу при отключении). События накапливаются в очереди с интервалом 100мс; они обрабатываются при вызове скриптом Wait.
Pascal: procedure OnTimer1;
Python: def on_timer1()
→ StealthEvent
Same as evTimer1 — a second independent timer channel.
Аналогичен evTimer1 — второй независимый канал таймера.
Pascal: procedure OnTimer2;
Python: def on_timer2()
→ StealthEvent
Sound played at coordinates.
Звук воспроизведён по координатам.
Pascal: procedure OnSound(Sound_ID: Word; X, Y: Word; Z: ShortInt);
Python: def on_sound(sound_id: int, x: int, y: int, z: int)
→ SoundEvent(sound_id, x, y, z)
Character died or was resurrected.
Персонаж умер или воскрешён.
Pascal: procedure OnDeath(Dead: Boolean);
Python: def on_death(dead: bool)
→ StealthEvent
Quest arrow appeared, moved, or disappeared.
Стрелка квеста появилась, переместилась или исчезла.
Pascal: procedure OnQuestArrow(X, Y: Word; Active: Boolean);
Python: def on_quest_arrow(x: int, y: int, active: bool)
→ StealthEvent
Party invitation received.
Получено приглашение в группу.
Pascal: procedure OnPartyInvite(Inviter_ID: Cardinal);
Python: def on_party_invite(inviter_id: int)
→ StealthEvent
Map pin action (marked point on a cartography map item).
Действие с маркером на карте (отмеченная точка на картографическом предмете).
Pascal: procedure OnMapPin(ID: Cardinal; Action, PinID: Byte; X, Y: Word);
Python: def on_map_pin(id: int, action: int, pin_id: int, x: int, y: int)
→ StealthEvent
Text entry dialog (looks like a gump, but technically is not).
Диалог ввода текста (выглядит как гамп, но технически им не является).
Pascal: procedure OnGumpTextEntry(Serial: Cardinal; Title: String;
InputStyle: Byte; MaxLength: Cardinal; Desc: String);
Python: def on_text_entry(serial: int, title: str, input_style: int,
max_length: int, desc: str)
→ StealthEvent
Graphical effect displayed (from packets 0x70 and 0xC0).
Графический эффект отображён (из пакетов 0x70 и 0xC0).
Pascal: procedure OnGraphicalEffect(SrcID: Cardinal; SrcX, SrcY: Word; SrcZ: SmallInt;
DstID: Cardinal; DstX, DstY: Word; DstZ: SmallInt;
EffectType: Byte; ItemID: Word; FixedDir: Byte);
Python: def on_effect(src_id: int, src_x: int, src_y: int, src_z: int,
dst_id: int, dst_x: int, dst_y: int, dst_z: int,
effect_type: int, item_id: int, fixed_dir: int)
→ StealthEvent
Event from Telegram or Discord messenger bot.
Событие от мессенджер-бота Telegram или Discord.
In Pascal (internal scripts), Sender is a TMessenger object — you can call methods on it directly. In Python (external scripts), the object cannot be transferred via IPC, so the messenger code is passed instead (see Messenger enum: 1 = Telegram, 3 = Discord).
В Pascal (внутренние скрипты) Sender — это объект TMessenger, на котором можно напрямую вызывать методы. В Python (внешние скрипты) объект не может быть передан через IPC, поэтому передаётся код мессенджера (см. Messenger enum: 1 = Telegram, 3 = Discord).
Pascal: procedure OnMessengerEvent(Sender: TMessenger; SenderNickName: String;
SenderId, ChatId: String;
EventMsg: String; EventCode: Byte);
Python: def on_messenger(mes_id: int, sender_name: str, sender_id: str,
chat_id: str, event_msg: str, event_code: int)
→ StealthEvent
Triggered when a global variable is set via SetGlobal('stealth', ...) or from an external script.
Срабатывает при установке глобальной переменной через SetGlobal('stealth', ...) или из внешнего скрипта.
Pascal: procedure OnSetGlobalVar(VarName: String; VarValue: String);
Python: def on_global_var(var_name: str, var_value: str)
→ StealthEvent
Fired when any mobile’s stats change. Values of -1 indicate that particular stat was not updated in this event (data comes from different packets: 0x2D provides all stats, 0xA1 only HP, 0xA2 only Mana, 0xA3 only Stamina).
Срабатывает при изменении статов любого мобайла. Значение -1 означает, что данный стат не обновлялся в этом событии (данные приходят из разных пакетов: 0x2D содержит все статы, 0xA1 — только HP, 0xA2 — только Mana, 0xA3 — только Stamina).
Pascal: procedure OnUpdateObjStats(ID: Cardinal; CurLife, MaxLife,
CurMana, MaxMana,
CurStam, MaxStam: Integer);
Python: def on_stats(id: int, cur_life: int, max_life: int,
cur_mana: int, max_mana: int,
cur_stam: int, max_stam: int)
→ StealthEvent
Global chat action/message.
Действие/сообщение глобального чата.
Pascal: procedure OnGlobalChat(MsgNum: Word; Param1, Param2: String);
Python: def on_global_chat(msg_num: int, param1: str, param2: str)
→ StealthEvent
Damage applied to a mobile.
Урон, нанесённый мобайлу.
Pascal: procedure OnWarDamage(ID: Cardinal; Damage: Word);
Python: def on_damage(id: int, damage: int)
→ StealthEvent
Context menu received for an object.
Получено контекстное меню для объекта.
Pascal: procedure OnContextMenu(ID: Cardinal);
Python: def on_context_menu(id: int)
→ ContextMenuEvent(serial)
| Event | Status | Replacement |
|---|---|---|
evUnicodeSpeech |
Deprecated | Use evSpeech |
evClilocSpeechAffix |
Deprecated | Use evClilocSpeech |
evDrawGamePlayer |
Deprecated | Use evDrawObject |
evUpdateChar |
Deprecated | Use evDrawObject |
evICQDisconnect |
Removed | — |
evICQConnect |
Removed | — |
evICQIncomingText |
Removed | — |
evICQError |
Removed | — |
evIRCIncomingText |
Removed | — |
evWindowsMessage |
Removed | — |
| Pascal Name | Python EventType |
Value |
|---|---|---|
evItemInfo |
EvItemInfo |
0 |
evItemDeleted |
EvItemDeleted |
1 |
evSpeech |
EvSpeech |
2 |
evMoveRejection |
EvMoveRejection |
4 |
evDrawContainer |
EvDrawContainer |
5 |
evAddItemToContainer |
EvAddItemToContainer |
6 |
evAddMultipleItemsInCont |
EvAddMultipleItemsInCont |
7 |
evRejectMoveItem |
EvRejectMoveItem |
8 |
evDrawObject |
EvDrawObject |
10 |
evMenu |
EvMenu |
11 |
evMapMessage |
EvMapMessage |
12 |
evAllow_RefuseAttack |
EvAllowRefuseAttack |
13 |
evClilocSpeech |
EvClilocSpeech |
14 |
evBuff_DebuffSystem |
EvBuffDebuffSystem |
17 |
evClientSendResync |
EvClientSendResync |
18 |
evCharAnimation |
EvCharAnimation |
19 |
evIncomingGump |
EvIncomingGump |
24 |
evTimer1 |
EvTimer1 |
25 |
evTimer2 |
EvTimer2 |
26 |
evSound |
EvSound |
28 |
evDeath |
EvDeath |
29 |
evQuestArrow |
EvQuestArrow |
30 |
evPartyInvite |
EvPartyInvite |
31 |
evMapPin |
EvMapPin |
32 |
evGumpTextEntry |
EvGumpTextEntry |
33 |
evGraphicalEffect |
EvGraphicalEffect |
34 |
evMessengerEvent |
EvMessengerEvent |
36 |
evSetGlobalVar |
EvSetGlobalVar |
37 |
evUpdateObjStats |
EvUpdateObjStats |
38 |
evGlobalChat |
EvGlobalChat |
39 |
evWarDamage |
EvWarDamage |
40 |
evContextMenu |
EvContextMenu |
41 |
Program EventDemo;
procedure OnSpeech(Text, SenderName: String; SenderID: Cardinal);
begin
AddToSystemJournal('Speech from ' + SenderName + ': ' + Text);
end;
procedure OnCharAnimation(ID: Cardinal; Action: Word);
begin
AddToSystemJournal('Animation: ID=$' + IntToHex(ID, 8) +
' Action=' + IntToStr(Action));
end;
procedure OnMultipleItems(ContainerID: Cardinal);
begin
AddToSystemJournal('Multiple items added to $' + IntToHex(ContainerID, 8));
end;
procedure OnUpdateStats(ID: Cardinal; CurLife, MaxLife, CurMana, MaxMana,
CurStam, MaxStam: Integer);
begin
if CurLife >= 0 then
AddToSystemJournal('HP update for $' + IntToHex(ID, 8) + ': ' +
IntToStr(CurLife) + '/' + IntToStr(MaxLife));
end;
begin
SetEventProc(evSpeech, 'OnSpeech');
SetEventProc(evCharAnimation, 'OnCharAnimation');
SetEventProc(evAddMultipleItemsInCont, 'OnMultipleItems');
SetEventProc(evUpdateObjStats, 'OnUpdateStats');
while True do
Wait(50);
end.
from py_astealth.stealth_enums import EventType
def speech_handler(text, sender_name, sender_id):
AddToSystemJournal(f'Speech {sender_name}({sender_id}): {text}')
def item_handler(item_id):
AddToSystemJournal(f'ItemInfo {item_id}')
SetEventProc(EventType.EvSpeech, speech_handler)
SetEventProc(EventType.EvItemInfo, item_handler)
while True:
Wait(100)
from py_astealth.stealth_enums import EventType
from py_astealth.stealth_events import SpeechEvent, ItemInfoEvent
SetEventCallback(EventType.EvSpeech)
SetEventCallback(EventType.EvItemInfo)
while True:
event = WaitForEvent(100)
if event is None:
continue
if isinstance(event, SpeechEvent):
AddToSystemJournal(f'Speech {event.sender_name}: {event.text}')
elif isinstance(event, ItemInfoEvent):
AddToSystemJournal(f'Item: {event.serial}')
else:
AddToSystemJournal(f'Event {event.id}: {event.arguments}')