Запись действий на кнопки (волшебные кнопки).

За помощь в написании материала огромная благодарность: Schatten и Vlad (участники нашего Discord)

Простой, но очень полезный метод записи нужных нам действий на нужные нам кнопки.

Но, для начала.. ОСТОРОЖНО! Говнокод! (И говнометод с ним же).
Нашёл тут у одного деятеля видео двухлетней давности.
И да, оно работает. Правда при использовании несколько раз у меня игра зависала 2 из 3 раз.
¯\_(ツ)_/¯

Он предлагает писать это в init.sqf

waitUntil{!(isNull (findDisplay 46))};
(findDisplay 46) displayAddEventHandler ["KeyDown", {hint str _this}];

потом запускать игру, получать значение кнопки.
После этого удалять это из init.sqf и писать туда вот это:

waitUntil {!(isNull (findDisplay 46))};
(findDisplay 46) displayAddEventHandler ["KeyDown", {
    switch (_this select 1) do {
        case 54: {          
            hint "Сообщение";
            (findDisplay 46) displayRemoveAllEventHandlers "KeyDown";
        };
    };
    false
}];

Почему это говнокод?
Ну, во-первых, он зачем-то пытается использовать init.sqf для получения значения кнопки (Консоль отладки! Слышал, не?).

Во-вторых, он по умолчанию предлагает удалять displayEventHandler, вопрос только зачем.. но не только его, а все (ВСЕ!) displayEventHandlers используя команду displayRemoveAllEventHandlers «KeyDown».

А если мы используем их для записи других действий на другие кнопки!?
Ура, мы только что снесли все записанные кнопки одной строчкой!
Причём даже если они были записаны другим скриптом.


Теперь в мир нормального:


Наш метод позволяет нам даже игру не открывать, чтобы написать этот код.
Достаточно зайти на страницу BIS Wiki в DIK Codes.

Сразу оговорюсь, что можно вместо константы писать её числовое значение, но это сильно портит читаемость кода для того кто его откроет. Возможно, ваш друг 😉 До того момента..
Ведь прочитать 54 и прочитать DIK_RSHIFT — вполне разные результаты понимания написанного.

Но, если нам не очень понятно там, то делаем так.
Вот это вот пишем в консоль отладки (Esc в игре):

(findDisplay 46) displayAddEventHandler ["KeyDown", "hint str _this"];

После этого нажимаем нужную нам кнопку и видим справа вверху хинт, где второе число будет нужное нам.
Разумеется каждая кнопка имеет уникальное значение.
Это значение мы можем использовать вместо констант, но с ним гораздо удобнее и нагляднее.

Но вместо этого мы можем перейти по ссылке DIK Codes , видим DIK нужной нам клавиши и используем его.
Нас интересует раздел DIK Codes for QWERTY.
В общем можно игру даже не запускать.​DIK_RSHIFT

А теперь, мы узнали константу (DIK_RSHIFT) интересующей нас кнопки, и нам нужно записать на неё какое-либо действие.

Да-да, сначала кажется немного замороченным, но давайте взглянем в код, и он становится куда более читаемым, когда там не какая-то цифра, а вполне логичное название связанное с кнопкой на клавиатуре.

В этом примере мы добавляем не одну кнопку, а сразу 3, при этом не плодим обработчиков событий.
Причём в первой кнопке (RSHIFT) я оставил возможность удаления того что мы туда привязали, если мы хотим её очистить (строка 20), при этом не снося все обработчики событий взмахом дубины.
Используем там где нам это необходимо.

Сразу отмечу, что в обработчик собтый displayAddEventHandler передаётся целых 5 параметров, а мы будем использовать всего 2 первых.
При этом местами мы их не можем менять.
Ссылка на полное описание обработчика событий keyDown https://community.bistudio.com/wiki/User_Interface_Event_Handlers#onKeyDown

Всё в одном обработчике.
Если написать displayRemoveEventHandler в любом его месте — то удалятся все записанные в этом обработчике привязки действий. (Пример в коде)

#include "\a3\ui_f\hpp\defineDIKCodes.inc"
#include "\a3\ui_f\hpp\defineResincl.inc"

_display = displayNull;

waitUntil {
    _display = findDisplay IDD_MISSION;

    !(isNull _display)
};


_display displayAddEventHandler ["KeyDown", {
    params ["_display", "_key"];
    
    switch (_key) do {
        case DIK_RSHIFT: {
            hint "Сообщение";
            // Если "включить" эту строку, то удалится весь обработчик события
            // а не только вариант с hint "Сообщение";
            //_display displayRemoveEventHandler ["KeyDown", _thisEventHandler];
        };

        case DIK_F5: {
            saveGame;
        };

        case DIK_F6: {
            loadGame;
        };        
    };
    false
}];

Здесь мы на правый Shift добавили hint с текстом «Сообщение».
На клавишу F5 мы добавили сохранение.
На клавишу F6 мы добавили загрузку последнего сохранения.

В начале кода мы ссылаемся на игровые файлы самой армы, подключая их через #include.
Там описаны те самые константы, что являются для нас более наглядными при чтении кода.

После этого мы находим нужный нам дисплей и после этого уже не ищем его каждый раз.
При этом мы обращаемся к тому же дисплею 46, но только опять же, по его константе IDD_MISSION.
Что является для нас более информативным при чтении кода.
Список всех таких IDD по этой ссылке.

Если мы хотим какое либо действие сделать удаляемым, а какие то нет, то нам следует написать два обработчика.
В разных обработчиках.

#include "\a3\ui_f\hpp\defineDIKCodes.inc"
#include "\a3\ui_f\hpp\defineResincl.inc"

_display = displayNull;

waitUntil {
    _display = findDisplay IDD_MISSION;

    !(isNull _display)
};

// Первый обработчик. Его нам нужно удалить после использования.
_display displayAddEventHandler ["KeyDown", {
    params ["_display", "_key"];
    
    switch (_key) do {
        case DIK_RSHIFT: {
            hint "Сообщение";
            // в данном случае удалит только обработчик с hint "Сообщение";
            _display displayRemoveEventHandler ["KeyDown", _thisEventHandler];
        };        
    };
    false
}];

// Второй обработчик. Который нам не надо удалять.
_display displayAddEventHandler ["KeyDown", {
    params ["_display", "_key"];
    
    switch (_key) do {
        case DIK_F5: {
            saveGame;
        };

        case DIK_F6: {
            loadGame;
        };        
    };
    false
}];

Вот такой гибкий, вполне простой (если открыть глаза) вариант добавления разных действий на разные кнопки, с возможностью удалять только определённые, а не сносить всё целиком.

Обсудить это можно в нашем Discord канале