Модуль клавиатурного ввода
В качестве следующего примера рассмотрим код модуля keyboard. Этот модуль можно скачать с репозитория модулей. Модуль позволяет организовать ввод пользователя с клавиатуры.
Идея модуля состоит в том, что клавиатурный ввод оформлен в виде специальной комнаты, в которой можно выполнить набор текста и вернуться к первоначальной комнате.
Комната, содержащая в себе логику клавиатурного ввода, носит имя @keyboard. Это системный объект, который не уничтожается при рестарте мира.
Для использования клавиатуры используется ссылка на системный объект (см. main3.lua):
p [[Как вас {@keyboard "Имя"|зовут}?]];Как видим, в качестве параметра передаётся информационная строка, которая будет отображена при вводе.
Объект @keyboard, соответственно, должен реализовать act обработчик, который перенесёт игрока в комнату клавиатурного ввода:
act = function(s, w, ...)
s.title = w or "?"
s.args = { ... }
walkin(s)
end;Кроме того, что здесь мы меняем название комнаты (title) и делаем переход (walkin), мы также запомнили все дополнительные аргументы, если они передавались в ссылке {@keyboard}. Зачем это нужно, будет ясно позднее.
Итак, осталось реализовать клавиатурный ввод. Для этого мы воспользуемся ссылками кнопок (для того, чтобы вводить текст можно было на устройствах без клавиатуры). И одновременно с этим, будем отслеживать нажатия на клавиши с помощью модуля keys (для удобства игроков на компьютерах с клавиатурой).
Формирование ссылок-клавиш осуществляется в decor. При этом, ссылки формируются в виде:
row = row.."{@kbdinput "..vv.."|"..input_esc(a).."}"..fmt.nb " ";input_esc() -- функция, реализованная в модуле, которая экранирует некоторые символы, типа { и }.
Как видим, это снова ссылки на системный объект, но теперь это уже @kbdinput. Этот объект специально создан для обработки событий от клавиш. Прежде чем мы перейдём к нему, рассмотрим вопрос использования модуля keys.
Для использования keys мы должны:
- определить onkey в комнате @keyboard;
- задать keys:filter()
Мы могли бы просто определить в модуле:
function keys:filter()
-- логика функции
endНо мы хотим, чтобы наш модуль мог работать совместно с каким-то другим применением модуля keys, поэтому при старте игры мы перехватываем старый обработчик keys:filter, заменяя его на свой. А потом, при деинициализации мира возвращаем старый обработчик обратно (когда он уже не нужен).
local hooked
local orig_filter
std.mod_start(function(load)
if not hooked then
hooked = true
orig_filter = std.rawget(keys, 'filter')
std.rawset(keys, 'filter', std.hook(keys.filter, function(f, s, press, key)
if std.here().keyboard_type then
return hook_keys[key]
end
return f(s, press, key)
end))
end
end)
std.mod_done(function(load)
hooked = false
std.rawset(keys, 'filter', orig_filter)
end)Здесь мы видим использование нескольких функций:
- std.rawget(таблица, имя) - получить элемент таблицы без генерации сообщения о несуществующей переменой (работа с переменными на низком уровне);
- std.rawset(таблица, имя, значение) - установить значение элемента таблицы без генерации сообщения о необъявленной переменной;
- std.hook(старая функция, функция перехвата) - создать функцию перехвата.
std.rawget/std.rawset -- это способ работы с переменными на самом низком уровне. Например, если бы в mod_start мы просто присвоили keys.filter = std.hook ..., то мы получили бы сообщение о том, что мы меняем объект, который не является переменной.
Итак, если клавиши нажимаются в момент нахождения в комнате @keyboard, то мы используем наш фильтр клавиш, если нет -- используется перехваченный фильтр клавиш (keys:filter).
Массив hook_keys содержит все те клавиши, которые мы перехватываем.
Объект @kbdinput реализует act, который занимается обработкой вводимых символов. Когда текст набран и ввод подтверждён клавишей ввод или соответствующей ссылкой выполняется следующий код:
walkback();
return std.call(std.here(), 'onkbd', _'@keyboard'.text, std.unpack(_'@keyboard'.args))Как видим, происходит вызов обработчика onkbd в комнате из которой мы пришли. В качестве первого параметра передаётся введённый текст. Затем передаются все те параметры, которые пользователь задал в ссылке @keyboard. Эти параметры помогут идентифицировать поле ввода, если мы хотим использовать несколько полей ввода в одной комнате.
Здесь используются следующие функции:
- std.unpack(таблица) -- превратить таблицу в набор аргументов;
- std.call(объект, метод, аргументы) -- вызвать обработчик объекта instead.
Теперь вы можете самостоятельно разобраться в остальных деталях модуля @keyboard.