Документация Расширения игры

Интерфейс и ввод

Кнопки и карточки выбора на холсте нажимаются и подсвечиваются при наведении; можно регистрировать горячие клавиши, выбирать точку кликом по земле, узнавать, куда указывает мышь и кого выбрал локальный игрок. Клики, горячие клавиши, применение способностей, полный текст чата, уход игроков — всё попадает в поток событий.

То, что рисует холст, теперь можно нажимать. Рантайм перехватывает ввод окна игры, и внешняя программа может:

ВозможностьКороткоПолучает ли это игра
Кликабельные элементы холстаКнопки, карточки выбора, панели: клик отправляет ui.click, при наведении — автоматическая подсветкаКлик по кнопке до игры не доходит
Горячие клавишиРегистрируете сочетания вроде F5 или ctrl+shift+Q, нажатие отправляет hotkeyМожно поглотить (вместе с символом, который порождает нажатие)
Клик по землеКлик по миру отправляет mouse.world с координатами на землеМожно поглотить («кликните, где поставить башню»)
Позиция мышиОбновляется каждый кадр: пиксели экрана, точка на земле под курсором, элемент холста под курсором—
ВыделениеКого выбрал локальный игрок; при любом изменении приходит selection.changed—

Всё это — локальный ввод + локальная отрисовка: в поток приказов ничего не попадает, поэтому безопасно и в многопользовательской игре. Но если обработчик меняет мир (создаёт юнитов, меняет характеристики), это по-прежнему только для одиночной игры.

Python: g.ui

ui = g.ui                                                   # при первом обращении рантайм перехватывает ввод окна
ui.button("shop", "Купить зелье (50 золота)", screen=(40, 300), on_click=lambda g, ev: buy(g))
c = ui.choice("Новый уровень! Выберите награду", [("Сила +5", "крепче"), ("Скорость атаки +20%", "бьёт чаще"), ("Призвать волка", "ещё один помощник")],
              pause=True, on_pick=lambda g, i: give(g, i))  # ряд карточек посреди экрана; pause=True — пока выбирают, игра на паузе
i = c.wait(timeout=30)                                      # можно и ждать с блокировкой (события при этом обрабатываются и не теряются)
ui.hotkey("F5", lambda g, ev: g.say(hero, "Понял!"))        # по умолчанию нажатие поглощается
ui.hotkey("ctrl+shift+Q", on_press=..., swallow=False)
ui.mouse(on_click, capture=True, buttons=("left", "right"))  # ловить клики по земле: сообщать о левой и правой кнопке и поглощать их
xy = ui.pick_point("Кликните по земле: где поставить башню?")  # блокирующая версия: следующий левый клик по земле -> (x, y); Esc или тайм-аут -> None
ui.cursor()                                                 # {'screen': (x, y), 'world': (x, y, z) или None, 'hover': 'shop'}
ui.toast("Идёт 3-я волна!", seconds=3)
ui.close()                                                  # убрать свои элементы и горячие клавиши; ввод окну возвращается, только если им не пользуется другая программа
g.close()                                                   # или отключиться целиком (можно и так: with Game(...) as g:)

Обработчики получают (g, ev) и срабатывают, когда вы вызываете g.events(), — раннеры ботов и игровых модов делают это каждый тик. Клики, для которых обработчик не задан, попадают в ui.clicks. Исключение в обработчике только записывается в лог и не мешает другим обработчикам и событиям.

Можно работать и напрямую с холстом: g.canvas.text(..., clickable=True, hover=цвет); клики приходят из потока событий, а ev.key — это key, заданный при отрисовке. Если нарисовать кликабельный элемент, перехват ввода включается автоматически — трогать g.ui заранее не нужно.

Запись горячих клавиш: F1 ~ F24, A ~ Z, 0 ~ 9, numpad0 ~ numpad9, space enter esc tab backspace insert delete home end pageup pagedown left up right down; впереди можно добавить ctrl+, shift+, alt+.

Буквы и цифры без модификаторов конфликтуют с вводом в чат и горячими клавишами самой игры. Лучше берите клавиши, которые игра не использует, вроде F5 ~ F8, или сочетания.

Новые события

В g.events() появились такие события (все поля — в протоколе W3P):

kindКогдаУдобные поля
ui.clickНажат интерактивный элемент холста.key — key на холсте, .button ('left' / 'right'), .mods — модификаторы
ui.hoverМышь зашла на элемент холста / ушла с него.key (при уходе — None)
hotkeyНажата зарегистрированная горячая клавиша.key — запись клавиши, .mods
mouse.worldКлик по миру, если включён перехват кликов по земле.x .y — координаты на земле, .button, .value (1 = клик поглощён)
selection.changedИзменилось выделение локального игрокаЮнитов берите через g.selection()
spell.castЮнит применил способность (способность ушла на перезарядку).spell — четырёхсимвольный код, .b — уровень, .value — перезарядка в секундах, .x .y — точка применения
messageВ окне сообщений на экране появилась строка.text — полный текст, .frame — в каком окне, .chat (если это чат)
player.leftИгрок вышел или удалён после поражения.player
game.endedВыход из матча—

Чат и экранные сообщения

То, что игрок пишет в чат, читается прямо из поля .chat события message:

for ev in g.events():
    if ev.kind == "message" and ev.chat and ev.chat["text"] == "-follow":
        ...                                    # ev.chat = {'channel': 'Всем', 'sender': 'имя игрока', 'text': '-follow'}

У g.messages() свой, независимый курсор, и в нём есть ещё и подсказки игры («Нужно больше ферм», «Здесь строить нельзя»). В боте по ним видно, почему команда не выполнилась.

Из других языков

  • Шлюз: методы ui.button, ui.choice, ui.hotkey, ui.mouse, ui.cursor доступны в шлюзе под теми же именами. Передать функцию-обработчик удалённо нельзя, поэтому клики и горячие клавиши приходят в потоке событий (событие ui.click содержит key).
  • Прямая запись в общую память: сначала отправьте семантическую команду input_enable (код операции W3P 74), и рантайм начнёт перехватывать ввод; в блоке ввода Local\War3Input_<pid> вы записываете таблицу горячих клавиш и переключатели мыши, а рантайм записывает обратно позицию мыши, точку на земле под курсором и элемент под курсором. Флаг 0x40 у элемента холста означает «интерактивный». Раскладка — в протоколе W3P.

Несколько программ одновременно

Моды, Farsight, MCP и каждый сеанс шлюза могут одновременно ставить кнопки и регистрировать горячие клавиши в одной игре, не мешая друг другу:

  • каждая программа регистрирует свои горячие клавиши и свой переключатель кликов по земле, а SDK сводит всё в одну таблицу и передаёт её рантайму. Каждая клавиша попадает в таблицу один раз, событие получают все, и каждый узнаёт свои горячие клавиши по клавише;
  • ui.close() убирает только своё, а ввод окну возвращается, лишь когда уходит последняя программа;
  • если программу завершили принудительно и она не успела прибраться, рантайм раз в 2 секунды проверяет, живы ли зарегистрированные программы; когда все они завершились, он убирает оставленные ими горячие клавиши и перехват кликов по земле, а их кнопки больше не перехватывают клики.

Замеры

2026-09-25, проверка в реальной игре на тестовом экземпляре, 16/16:

  • клик по кнопке → ui.click + обработчик, счётчик перехватов в рантайме +1 (игра этот клик не получила); клик мимо кнопки ничего не вызывает;
  • F6 → hotkey; клик по земле → mouse.world (поглощён);
  • создать паладина и выбрать его → selection.changed, g.selection() совпадает; применить «Божественный щит» → spell.cast('AHds', 1, 35.0);
  • текст карты → message; чат → message, в .chat разобраны отправитель и текст;
  • поражение компьютерного игрока → player.left; завершение матча → game.ended.

Клики по кнопкам настоящей мышью и подсветку при наведении тоже проверили по одному.

Ограничения и замечания

  • Позиция берётся от настоящей мыши: игра сама читает позицию по системному курсору, поэтому наведение и cursor() отражают настоящую мышь. Перехват касается только нажатий.
  • Рисуется под указателем мыши: Warcraft каждый кадр рисует указатель как часть картинки. Холст и облачки над головой рисуются до того шага, на котором игра рисует указатель: они перекрывают интерфейс игры, а указатель перекрывает их. Только если в этом кадре указатель не рисуется (скрыт или идёт заставка), они рисуются последним шагом.
  • Системное масштабирование: если в своих тестах вы отправляете клики оконными сообщениями, координаты от процесса без поддержки DPI система увеличивает (при масштабе 150% замерено ×1.5). Объявите в тестовой программе поддержку DPI. На клики живого человека это не влияет.
  • В первый раз нужно прогреть шрифты — около 1 секунды. Пока кнопки ещё не нарисованы, нажать их нельзя.
  • Вне матча клики по земле не сообщаются: в главном меню и на экране итогов mouse.world не отправляется и не поглощается.
  • Если нажатие было поглощено, а до отпускания вы переключились на другую программу или увели мышь за пределы окна, состояние всё равно сбрасывается, и следующее отпускание уже не поглощается.
  • В 1.27 нет функций для создания новых фреймов интерфейса игры (они появились только в 1.31): кнопки и карточки здесь рисует рантайм, стиль может быть любым, но в собственной иерархии меню игры они не появляются.