Создание меню в SourcePawn

Тема в разделе "Программирование / Скриптинг", создана пользователем The End Is Near..., 7 мар 2013.

  1. The End Is Near...

    The End Is Near... Russian Roulette

    Сообщения:
    893
    Симпатии:
    659
    Создание меню в SourcePawn

    SourceMod имеет обширный API для создания и отображения меню клиентам.

    Стили
    Объект верхнего уровня MenuStyle (IMenuStyle в C++). Есть два стиля отображения меню
    • Стиль Valve - так называемое "ESC" меню; 8 элементов на странице
    • Стиль Radio - так называемое "AMX" меню; 10 элементов на странице
    Каждый MenuStyle имеет свои собственные правила и свойства. Два разных меню могут существовать на экране игрока как Valve меню и Radio меню. В то же время, SourceMod будет в состоянии управлять этими меню. Это связано с тем, что каждый стиль отслеживает свое меню отдельно.

    Панели
    Объект с более низким уровня интерфейсом Panels (IMenuPanel в C++). Панели позволяют сделать отображение, используя лишь только текст т.е. не используя выделительных пунктов (Select).
    Панели отображаются временно. Хотя они могут быть сохранены на неопределенный срок, не рекомендуется это делать.
    Стиль Valve
    • Максимальное количество элементов на странице 8
    • Заблокированные элементы не могут быть осуществлены
    • Raw текст не может быть осуществлен
    • Осуществление между текстом пространства не добавят возможности добавить в панель больше символов
    • Клиент должен будет нажать кнопку "ESC" или, по крайней открыть консоль для просмотра меню
    Стиль Radio
    • Максимальное количество элементов на странице 10
    • Оглавление белое, элементы желтые, но если их отключить, то они будут белыми
    • Элемент '0' всегда белый. Это означает, что навигационные кнопки управления всегда белого цвета

    Меню
    Есть простые Menu (IBaseMenu в C++). Эти вспомогательные объекты, предназначенные для хранения меню на основе выбора элементов. В отличие от низкого уровня панелей, меню могут содержать множество элементов, а также могут содержать только те пункты, которые выбираются (т. е. не содержат "белого" текста).
    Они делятся на две категории:
    Не пронумерованные - меню содержит в себе только лишь определенное количество элементов.
    Нет пунктов навигации (Вперед; Назад). Исключение: Выход.
    • [*]Назад (Previous) - Используется для возвращения просмотренных элементов; Перелистываемые страницы назад
      • Стиль Valve: Позиция в меню: 6
      • Стиль Radio: Позиция в меню: 8
      [*]Далее (Next) - Используется для просматривания следующих страниц; Перелистывание страницы вперед
      • Стиль Valve: Позиция в меню: 7
      • Стиль Radio: Позиция в меню: 9
      [*]Выход (Exit) - Используется для выхода из меню
      • Стиль Valve: Позиция в меню: 8
      • Стиль Radio: Позиция в меню: 10


    Цель меню - Упростить процедуру хранения и отображения информации. Таким образом, меню не позволяют добавить raw текст, так как это значительно усложняет отображение алгоритма.
    Примечание. C++ API поддерживает подключение IBaseMenu процедуры отображения и добавления raw текста, это будет добавлено в сценарии API в ближайшее время.
    Внутри меню отображение используется с помощью RenderMenu алгоритма. Этот алгоритм создает временную панель и наполняет ее элементы из меню. Эта панель будет отображаться на клиенте. Алгоритм пытается создать свободное перемещение между всеми меню, и во всех стилях. Таким образом, любое меню отображается с помощью IBaseMenu класса, или меню Handle будет отображаться и использоваться так же как и меню Panel API.

    Обзор
    Меню являются системой обратного вызова. Каждый обратный вызов представляет собой действие, которое происходит во время всего цикла действия меню. Цикл состоит из некоторых уведомлений:
    • Начало действия уведомлений
      • Отображение уведомления клиенту
      • Выбор пункта в меню
    • Конец действия уведомлений

    Спецификация
    Для C++, IBaseMenu всегда доступен. Для SourcePawn, Menu Handle и MenuAction всегда устанавливаются в MenuHandler обратного вызова. В отличие от C++, API позволяет SourcePawn выполнять определенные действия, если только меню было запрошено во время создания. Это оптимизация. Тем не менее, некоторые действия не могут быть предотвращены создания.
    • Start. Создание меню, причем OnMenuEnd гарантированно будет вызываться.
      • MenuAction_Start() в SourcePawn. Это действие не срабатывает при вызове.
        • param1: Игнорирование. Всегда 0
        • param2: Игнорирование. Всегда 0
      Display. Отображение меню клиенту.
      • MenuAction_Display в SourcePawn. Это действие не срабатывает при вызове.
        • param1: Индекс клиента, которому показано меню
        • param2: Handle в меню
      Select. Выбор пункта меню.
      • MenuAction_Select в SourcePawn. Это действие срабатывает, не смотря на вызов.
        • param1: Индекс клиента, который выбрал пункт
        • param2: Индекс пункта, который выбрал клиент
      Cancel. Отмена показа меню
      • MenuAction_Cancel в SourcePawn. Это действие срабатывает, не смотря на вызов.
        • param1: Индекс клиента, который отменил показ меню
        • param2: Код причины отмены показа меню
      End. Окончание показа меню
      • MenuAction_End в SourcePawn. Это действие всегда срабатывает, то ли просьба или нет.
        • param1: Причина окончания показа меню
        • param2: Если param1 был MenuEnd_Cancelled, это содержит причину отмены показа меню

    Панели
    Для панелей, функции обратного вызова всегда меняются. Для SourcePawn, Menu Handle всегда будет INVALID_HANDLE.
    Select. Выбор пункта в панели
    • MenuAction_Select в SourcePawn.
      • param1: Индекс клиента, который выбрал пункт
      • param2: Индекс пункта, который выбрал клиент
    Cancel. Отмена показа панели
    • MenuAction_Cancel в SourcePawn.
      • param1: Индекс клиента, который отменил показ панели
      • param2: Код причины отмены показа панели

    Пример
    Разберем создание панели и меню с вопросами
    Меню
    PHP:
    // Подключаем библиотеки
    #include <sourcemod>

    // Старт плагина
    public OnPluginStart()
    {
        
    // Регистрируем команду, введя которую нам отобразится меню
        
    RegConsoleCmd("menu"Menu)
    }

    // Обрабатываем команду
    public Action:Menu(clientargs)
    {
        
    // Создаем Handle меню
        // MenuHandler - Значит что, нажав на один из пунктов, произойдет дальнейшее действие
        // То есть, нажав к примеру на "Да", нам выведется в консоль о том, что вы выбрали именно этот пункт
        
    new Handle:menu CreateMenu(MenuHandler)
        
    SetMenuTitle(menu"Вы любите Counter-Strike:Source?")
        
    AddMenuItem(menu"yes""Да")
        
    AddMenuItem(menu"no""Нет")
        
    SetMenuExitButton(menufalse)
        
    DisplayMenu(menuclient20)
     
        return 
    Plugin_Handled
    }

    public 
    MenuHandler(Handle:menuMenuAction:actionparam1param2)
    {
        
    /* Если пункт меню был выбран, выводим в его консоль об этом */
        
    if (action == MenuAction_Select)
        {
            
    decl String:info[32]
            new 
    bool:found GetMenuItem(menuparam2infosizeof(info))
            
    PrintToConsole(param1"Вы выбрали пункт: %d (Найдено: %d Информация: %s)"param2foundinfo)
        }
        
    /* Если клиент отменил показ меню, выводим в консоль сервера */
        
    else if (action == MenuAction_Cancel)
        {
            
    PrintToServer("Клиент %d Отменил показ меню.  Причина: %d"param1param2)
        }
        
    /* Если показ меню закончился, закрываем Handle */
        
    else if (action == MenuAction_End)
        {
            
    CloseHandle(menu)
        }
    }
    Обратите внимание на несколько очень важных примечаний
    • Один из пунктов Select или Cancel будет направлен в действие обрабатывания меню
    • Пункт End всегда будет направлен в действие обрабатывания меню
    • Уничтожение меню следует производить только, если клиент отменил меню, или оно закончилось
    • В меню, по умолчанию, есть пункт выхода
    • Продолжительно показа меню зависит от DisplayMenu

    Панель
    PHP:
    // Подключаем библиотеки
    #include <sourcemod>

    // Старт плагина
    public OnPluginStart()
    {
        
    // Регистрируем команду, введя которую нам отобразится меню
        
    RegConsoleCmd("panel"Panel)
    }

    // Обрабатываем команду 
    public Action:Panel(clientargs)
    {
        
    // Создаем Handle панели
        
    new Handle:panel CreatePanel();
        
    SetPanelTitle(panel"Вы любите спать?")
        
    DrawPanelItem(panel"Да")
        
    DrawPanelItem(panel"Нет")
     
        
    SendPanelToClient(panelclientPanelHandler20)
     
        
    CloseHandle(panel)
     
        return 
    Plugin_Handled
    }

    public 
    PanelHandler(Handle:menuMenuAction:actionparam1param2)
    {
        if (
    action == MenuAction_Select)
        {
            
    PrintToConsole(param1"Вы выбрали пункт: %d"param2)
        } 
        else if (
    action == MenuAction_Cancel
        {
            
    PrintToServer("Клиент %d отменил показ панели. Причина: %d"param1param2)
        }
    }
    Некоторые примечания
    • Есть лимит добавления элементов в панель
    • Возможность уничтожения панели после окончания отображения
    • У обработчика всегда есть информация (Отменили показ панели;Выбрали пункт)

    Расширенное меню
    Теперь рассмотрим более сложный пример. Меню, в котором элементы расположены на нескольких страницах.
    Суть плагина: Отображение карт, с последующей сменой при выборе
    PHP:
    new Handle:g_MapMenu INVALID_HANDLE
     
    public OnPluginStart()
    {
        
    RegConsoleCmd("menu_changemap"Command_ChangeMap);
    }
     
    public 
    OnMapStart()
    {
        
    g_MapMenu BuildMapMenu();
    }
     
    public 
    OnMapEnd()
    {
        if (
    g_MapMenu != INVALID_HANDLE)
        {
            
    CloseHandle(g_MapMenu);
            
    g_MapMenu INVALID_HANDLE;
        }
    }
     
    Handle:BuildMapMenu()
    {
        
    /* Открытие файла */
        
    new Handle:file OpenFile("maplist.txt""rt");
        if (
    file == INVALID_HANDLE)
        {
            return 
    INVALID_HANDLE;
        }
     
        
    /* Создание Handle меню */
        
    new Handle:menu CreateMenu(Menu_ChangeMap);
        new 
    String:mapname[255];
        while (!
    IsEndOfFile(file) && ReadFileLine(filemapnamesizeof(mapname)))
        {
            if (
    mapname[0] == ';' || !IsCharAlpha(mapname[0]))
            {
                continue;
            }
            new 
    len strlen(mapname);
            for (new 
    i=0i<leni++)
            {
                if (
    IsCharSpace(mapname[i]))
                {
                    
    mapname[i] = '\0';
                    break;
                }
            }
            
    /* Проверка на то, что карта действительна */
            
    if (!IsMapValid(mapname))
            {
                continue;
            }
            
    /* Добавление в меню */
            
    AddMenuItem(menumapnamemapname);
        }
        
    /* Закрытие Handle */
        
    CloseHandle(file);
     
        
    /* Заголовок */
        
    SetMenuTitle(menu"Please select a map:");
     
        return 
    menu;
    }
     
    public 
    Menu_ChangeMap(Handle:menuMenuAction:actionparam1param2)
    {
        if (
    action == MenuAction_Select)
        {
            new 
    String:info[32];
     
            
    /* Получение информации пункта */
            
    new bool:found GetMenuItem(menuparam2infosizeof(info));
     
            
    /* Информация о выборе клиента */
            
    PrintToConsole(param1"You selected item: %d (found? %d info: %s)"param2foundinfo);
     
            
    /* Смена карты */
            
    ServerCommand("changelevel %s"info);
        }
    }
     
    public 
    Action:Command_ChangeMap(clientargs)
    {
        if (
    g_MapMenu == INVALID_HANDLE)
        {
            
    PrintToConsole(client"The maplist.txt file was not found!");
            return 
    Plugin_Handled;
        }    
     
        
    DisplayMenu(g_MapMenuclientMENU_TIME_FOREVER);
     
        return 
    Plugin_Handled;
    }
    Меню будет выглядеть так
    [​IMG] [​IMG] [​IMG]
    Примечания
    • Автоматическое отображение или скрытие элементов управления
    • Время, указанное для отображения клиенту меню, не меняется при использовании меню
    • Позиции элементов управления не могут быть изменены

    Голосование
    Голосование в SourceMod имеет сходные черты с меню
    • MenuAction_VoteStart. Начало голосования
    • MenuAction_VoteEnd. Конец голосования. Клиенты отменили, проголосовали или оставили не выбрали один из элементов
    • MenuAction_VoteCancel. Клиент отменил показ голосования. Причина отмены будет содержаться в параметре param1
    Стоить отметить некоторые примечания

    • Выбор отключившегося клиента недействителен
    • Результаты голосования вы должны самостоятельно получать

    Простое голосование
    PHP:
    public Handle_VoteMenu(Handle:menuMenuAction:actionparam1param2)
    {
        if (
    action == MenuAction_End)
        {
            
    /* Закрытие Handle, если меню стало неактивным */
            
    CloseHandle(menu);
        } else if (
    action == MenuAction_VoteEnd) {
            
    /* 0 = Да, 1 = Нет */
            
    if (param1 == 0)
            {
                new 
    String:map[64]
                
    GetMenuItem(menuparam1mapsizeof(map))
                
    ServerCommand("changelevel %s"map);
            }
        }
    }
     
    DoVoteMenu(const String:map[])
    {
        if (
    IsVoteInProgress())
        {
            return;
        }
     
        new 
    Handle:menu CreateMenu(Handle_VoteMenu)
        
    SetMenuTitle(menu"Сменить карту на %s?"map)
        
    AddMenuItem(menumap"Да")
        
    AddMenuItem(menu"no""Нет")
        
    SetMenuExitButton(menufalse)
        
    VoteMenuToAll(menu20);
    }
    Расширенное голосование
    PHP:
    public Handle_VoteMenu(Handle:menuMenuAction:actionparam1param2)
    {
        if (
    action == MenuAction_End)
        {
            
    /* Закрытие Handle */
            
    CloseHandle(menu);
        }
    }
     
    public 
    Handle_VoteResults(Handle:menu
                
    num_votes
                
    num_clients
                const 
    client_info[][2], 
                
    num_items
                const 
    item_info[][2])
    {
        
    /* Поиск элемента, набравшего больше всего голосов */
        
    new winner 0;
        if (
    num_items 1
            
    && (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))
        {
            
    winner GetRandomInt(01);
        }
     
        new 
    String:map[64]
        
    GetMenuItem(menuitem_info[winner][VOTEINFO_ITEM_INDEX], mapsizeof(map))
        
    ServerCommand("changelevel %s"map)
    }
     
    DoVoteMenu(const String:map[])
    {
        if (
    IsVoteInProgress())
        {
            return;
        }
     
        new 
    Handle:menu CreateMenu(Handle_VoteMenu)
        
    SetVoteResultCallback(menuHandle_VoteResults)
        
    SetMenuTitle(menu"Сменить карту на %s?"map)
        
    AddMenuItem(menumap"Да")
        
    AddMenuItem(menu"no""Нет")
        
    SetMenuExitButton(menufalse)
        
    VoteMenuToAll(menu20);
    }
    Перевод фраз в голосовании
    PHP:
    public Handle_VoteMenu(Handle:menuMenuAction:actionparam1param2)
    {
        if (
    action == MenuAction_End)
        {
            
    /* Закрытие Handle */
            
    CloseHandle(menu);
        } 
        else if (
    action == MenuAction_VoteEnd
        {
            
    /* 0=yes, 1=no */
            
    if (param1 == 0)
            {
                new 
    String:map[64]
                
    GetMenuItem(menuparam1mapsizeof(map))
                
    ServerCommand("changelevel %s"map);
            }
        } 
        else if (
    action == MenuAction_DisplayItem
        {
            
    /* Получение строки в меню, после получения будем его использовать как фразу для перевода */
            
    decl String:display[64];
            
    GetMenuItem(menuparam2""0_displaysizeof(display));
     
            
    /* Перевод текста, отображенный клиенту */
            
    decl String:buffer[255];
            
    Format(buffersizeof(buffer), "%T"displayparam1);
     
            return 
    RedrawMenuItem(buffer);
        } 
        else if (
    action == MenuAction_Display
        {
            
    /* Handle панели является вторым параметром */
            
    new Handle:panel Handle:param2;
     
            
    /* Получение имени карты */
            
    decl String:map[64];
            
    GetMenuItem(menu0mapsizeof(map));
     
            
    /* Фраза для перевода */
            
    decl String:buffer[255];
            
    Format(buffersizeof(buffer), "%T""Change map to?"clientmap);
     
            
    SetPanelTitle(panelbuffer);
        }
    }
     
    DoVoteMenu(const String:map[])
    {
        if (
    IsVoteInProgress())
        {
            return;
        }
     
        new 
    Handle:menu CreateMenu(Handle_VoteMenuMenuAction_DisplayItem|MenuAction_Display)
        
    SetMenuTitle(menu"Сменить карту на %s?"map)
        
    AddMenuItem(menumap"Да")
        
    AddMenuItem(menu"no""Нет")
        
    SetMenuExitButton(menufalse)
        
    VoteMenuToAll(menu20);
    }
    Источник
    Оригинал
     
    Hejter, [[[[KaZaK]]]], Sergey Grinko и 7 другим нравится это.
  2. Серый™

    Серый™ CS:S Server

    Сообщения:
    2.558
    Симпатии:
    1.143
    The End Is Near..., хороший гайд, продолжай в том-же духе))
     
  3. semjef

    semjef semjef.ru

    Сообщения:
    1.031
    Симпатии:
    473
    Серый™, скорее "хороший перевод"
     
  4. Sergey Grinko

    Sergey Grinko

    Сообщения:
    140
    Симпатии:
    12
    Как сделать чтобы после AddMenuItem можно было добавить строчку с текстом (чтобы у нее не было цифры в начале, и она была не некликабельной).

    Код:
    PHP:
        new String:showcd[256];
        
    Format(showcd,sizeof(showcd),"У вас %d кредитов"credits[userid]);
        new 
    Handle:menu CreateMenu(handler_menu);
        
    SetMenuTitle(menu"Магазин:\n \n");
        
    AddMenuItem(menu"1""Купить бонус за кредиты"ITEMDRAW_DEFAULT);
        
    AddMenuItem(menu"0""Обмен фрагов на кредиты"ITEMDRAW_DEFAULT);
        
    SetMenuExitButton(menutrue);
        
    DisplayMenu(menuuserid25);
     
  5. Scarface_slv

    Scarface_slv

    Сообщения:
    228
    Симпатии:
    47
    Можно с панелью, только это уже другое меню надо DrawPanelText("текст") так не будет цифр.
    "некликабельной" вместо ITEMDRAW_DEFAULT, ITEMDRAW_DISABLED
    Насчет убрать цифры в твоем меню я не знаю если только перед текстом знак \n и то будут цифры наверно
     
    Последнее редактирование: 8 апр 2013
  6. The End Is Near...

    The End Is Near... Russian Roulette

    Сообщения:
    893
    Симпатии:
    659
    AddMenuItem(menu, "0", "Обмен фрагов на кредиты\nПункт без цифры", ITEMDRAW_DEFAULT);
    Пробуй так.
     
  7. Sergey Grinko

    Sergey Grinko

    Сообщения:
    140
    Симпатии:
    12
    Очень не красиво... так еще белой не сделать.
     
  8. The End Is Near...

    The End Is Near... Russian Roulette

    Сообщения:
    893
    Симпатии:
    659
    Sergey Grinko
    В смысле белой не сделать?
     
  9. Sergey Grinko

    Sergey Grinko

    Сообщения:
    140
    Симпатии:
    12
    Ну кнопка не кликабельная ;)
    Тоесть я так понял, что типа такого не сделать?
    [​IMG]
     
  10. The End Is Near...

    The End Is Near... Russian Roulette

    Сообщения:
    893
    Симпатии:
    659
    Так и будет так то, \n переносит на новую строку без цифры
     
    Серый™ и Sergey Grinko нравится это.
  11. Sergey Grinko

    Sergey Grinko

    Сообщения:
    140
    Симпатии:
    12
    Так она будет подсвечивать при нажатие. И белой как на картинке ее не сделать
     
  12. ☆★☆БАТЯ☆★☆™

    ☆★☆БАТЯ☆★☆™

    Сообщения:
    2.585
    Симпатии:
    1.341
    Sergey Grinko, Ты пробовал?
     
    Sergey Grinko нравится это.
  13. Sergey Grinko

    Sergey Grinko

    Сообщения:
    140
    Симпатии:
    12
    Ого, и правда. Не ожидал ;)
    Все таки очень странный язык SourcePawn.
    [​IMG]