[SourcePawn] Урок 7 - Панели и Меню

Nekro

Терра инкогнита
Сообщения
4,025
Реакции
2,260
@Nekro, не вижу создания меню: у тебя тут только его каллбэк.
И ты его удаляешь в MenuAction_End. Для чего ты вообще его хэндл сохраняешь, если для удаления это не нужно в твоём примере?
Дополнил
Для чего ты вообще его хэндл сохраняешь, если для удаления это не нужно в твоём примере?
Так я же и хочу узнать как надо
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
4 раза за раунд это не такая большая нагрузка.
Но если у тебя реально большое меню, при том игроки исключительно русскоговорящие и так сильно нужна оптимизация, то можно и не удалять. Достаточно одного хендла меню.
 

Nekro

Терра инкогнита
Сообщения
4,025
Реакции
2,260
4 раза за раунд это не такая большая нагрузка.
Но если у тебя реально большое меню, при том игроки исключительно русскоговорящие и так сильно нужна оптимизация, то можно и не удалять. Достаточно одного хендла меню.
Так мой код приемлем? Или удаление там вообще не нужно?
 

Dragokas

Добрая душа
Сообщения
229
Реакции
213
Смотря для чего приемлем.
Если ты спрашиваешь, грамотно ли он написан / нет ли утечек..., то да, более-менее грамотно за исключением пары вещей:
1) В нём не нужен глобально объявленный хендл. Оптимизации он практически вообще никакой не даёт.
2) DisplayMenu можно было бы заменить на methodmap, чтобы всё было в одном стиле, т.е.: hMenu.Display(client, MENU_TIME_FOREVER);
3) SetTitle использовано некорректно. См. синтаксис в хелпе. hMenu.SetTitle("Быть или не быть?");

Если ты спрашиваешь про оптимизацию, можно ли создать меню один раз и юзать всегда? Да, можно, но тогда у тебя все клиенты будут получать Title и менюшки на одном языке.
В этом случае, в OnPluginStart создаешь меню, и в FMenu() просто отображаешь его клиенту: hMenu.Display, соответственно в MenuAction_End его хендл удалять не нужно.
Можно конечно через MenuAction_DisplayItem и MenuAction_Display вызывать RedrawMenuItem, перерисовывая пункты с нужным клиенту языком для поддержки мультиязычности, но в реале это может нивелировать все потуги к сделанной выше оптимизации. Если меню небольшое, я бы не парил голову, сделал бы как все, по первому варианту.
Сообщения автоматически склеены:

Ещё для оптимизации, можно как вариант, создавать глобальный хендл с объектом menu в OnPluginStart, но заполнять его позже, уже в FMenu, предварительно сделав ему очистку предудыщего содержимого через hMenu.RemoveAllItems(); (если важна поддержка многоязычности, либо если содержимое должно меняться).
 
Последнее редактирование:

Grey83

не пишу плагины с весны 2022
Сообщения
8,519
Реакции
4,979
@Nekro, если не нужна поддержка переводов, то вот этого достаточно, кмк:
C-подобный:
Menu
    hMenu;

public void OnPluginStart()
{
    HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);

    hMenu = new Menu(Menu_Choice);
    hMenu.SetTitle("Быть или не быть?");
    hMenu.AddItem("", "Быть");
    hMenu.AddItem("", "Не быть");
    hMenu.ExitButton = false;
}

public void Event_PlayerDeath(Handle event, const char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(GetEventInt(event, "userid"));
    if(client && !IsFakeClient(client)) hMenu.Display(client, MENU_TIME_FOREVER);
}

public int Menu_Choice(Menu menu, MenuAction action, int client, int item)
{
    if(action == MenuAction_Select && !item) FunctionMenu(client);
}
 

Deraxus

Участник
Сообщения
190
Реакции
24
C-подобный:
hPanel.Send(iClient, MenuHandler_MyPanel, 20);
Подскажите пожалуйста, что в этом блоке кода значит MenuHandler_MyPanel? Метод отправляет панель игроку, первый аргумент кому отправить, последний насколько отправить, а этот аргумент за что отвечает?
 

Young <

Now, finally free
Сообщения
1,263
Реакции
505
C-подобный:
hPanel.Send(iClient, MenuHandler_MyPanel, 20);
Подскажите пожалуйста, что в этом блоке кода значит MenuHandler_MyPanel? Метод отправляет панель игроку, первый аргумент кому отправить, последний насколько отправить, а этот аргумент за что отвечает?
Хендлер панели aka функция, которая будет вызываться, когда над панелью будет выполнено какое-то действие.
 

Ganter1234

Участник
Сообщения
1,149
Реакции
667
C-подобный:
hPanel.Send(iClient, MenuHandler_MyPanel, 20);
Подскажите пожалуйста, что в этом блоке кода значит MenuHandler_MyPanel? Метод отправляет панель игроку, первый аргумент кому отправить, последний насколько отправить, а этот аргумент за что отвечает?
Ответ
 

defron

Участник
Сообщения
342
Реакции
138
else if (iMenu == 3)
{
туть
}

Как сделать, чтобы при выборе пункта в меню использовалась команда? Допустим жму 2, и используется sm_admin
 

defron

Участник
Сообщения
342
Реакции
138
@defron, FakeClientCommand например.
FakeClientCommand(param1, "sm_admin");
Сообщения автоматически склеены:

Опять я, можно ли как то меню самому разделить на страницы? У меня на первой есть 5 пунктов, как ее перенести на след страницу, чтобы уже 6 был на другой. Т.е. в меню нажать 8. Далее будет
 

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #52
@defron, меню само разделяется на страницы. Если ты именно Menu используешь, а не Panel.
 

R1KO

fuck society
Сообщения
9,457
Реакции
7,786
  • Команда форума
  • #53
FakeClientCommand(param1, "sm_admin");
Сообщения автоматически склеены:

Опять я, можно ли как то меню самому разделить на страницы? У меня на первой есть 5 пунктов, как ее перенести на след страницу, чтобы уже 6 был на другой. Т.е. в меню нажать 8. Далее будет
Если надо изменить кол-во элементов на странице то SetMenuPagination · menus · SourceMod Scripting API Reference
 

data7

Участник
Сообщения
1
Реакции
0
как лучше сделать проверку в какой команде игрок чтобы ошибки не было и меню удалилось, если игрок за CT и он не выбрал пункт после респавна он перешел за Т не смог выбрать вариант из меню
C-подобный:
public void Show_TestMenu(int iClient)
{
    if(GetClientTeam(iClient) != 3) return;
 
    Menu hMenu = new Menu(TestMenu_CallBack);
    hMenu.ExitBackButton = false;
    hMenu.SetTitle("Выберите вариант");

    hMenu.AddItem("1", "вариант первый");
    hMenu.AddItem("2", "вариант второй");

    hMenu.Display(iClient, 0);
}
Первый вариант не знаю почему но меню вроде удаляется по факту а в памяти не знаю
C-подобный:
public int Test_CallBack(Menu hMenu, MenuAction action, int iClient, int iItem)
{
    if(GetClientTeam(iClient) == 3)
    {
        switch(action)
        {
            case MenuAction_End:
            {
                delete hMenu;
            }
            case MenuAction_Select:
            {
                case 0: функция первая
                case 1: функция вторая
            }
        }
    }
}
второй вариант вроде логично но вопрос не будет конфликтовать ведь игрок выбрал пункт но его не пустило дальше
C-подобный:
public int Test_CallBack(Menu hMenu, MenuAction action, int iClient, int iItem)
{
    switch(action)
    {
        case MenuAction_End:
        {
            delete hMenu;
        }
        case MenuAction_Select:
        {
            if(GetClientTeam(iClient) != 3) return;
            case 0: функция первая
            case 1: функция вторая
        }
     }
}
подскажите как лучше его реализовать?
 
Последнее редактирование:

danil253467

💘Italo-Disco & Neon One Love💘
Сообщения
90
Реакции
52
как лучше сделать проверку в какой команде игрок чтобы ошибки не было и меню удалилось, если игрок за CT и он не выбрал пункт после респавна он перешел за Т не смог выбрать вариант из меню
C-подобный:
public void Show_TestMenu(int iClient)
{
    if(GetClientTeam(iClient) != 3) return;
 
    Menu hMenu = new Menu(TestMenu_CallBack);
    hMenu.ExitBackButton = false;
    hMenu.SetTitle("Выберите вариант");

    hMenu.AddItem("1", "вариант первый");
    hMenu.AddItem("2", "вариант второй");

    hMenu.Display(iClient, 0);
}
Первый вариант не знаю почему но меню вроде удаляется по факту а в памяти не знаю
C-подобный:
public int Test_CallBack(Menu hMenu, MenuAction action, int iClient, int iItem)
{
    if(GetClientTeam(iClient) == 3)
    {
        switch(action)
        {
            case MenuAction_End:
            {
                delete hMenu;
            }
            case MenuAction_Select:
            {
                case 0: функция первая
                case 1: функция вторая
            }
        }
    }
}
второй вариант вроде логично но вопрос не будет конфликтовать ведь игрок выбрал пункт но его не пустило дальше
C-подобный:
public int Test_CallBack(Menu hMenu, MenuAction action, int iClient, int iItem)
{
    switch(action)
    {
        case MenuAction_End:
        {
            delete hMenu;
        }
        case MenuAction_Select:
        {
            if(GetClientTeam(iClient) != 3) return;
            case 0: функция первая
            case 1: функция вторая
        }
     }
}
подскажите как лучше его реализовать?
Первый вариант предпочтительнее, т.к, если я все правильно понимаю, после выполнения MenuAction_Select происходит MenuAction_End, в котором и происходит удаление хэндла, т.е все должно быть в порядке
 

Svyatoy

Участник
Сообщения
335
Реакции
137
Первый вариант предпочтительнее, т.к, если я все правильно понимаю, после выполнения MenuAction_Select происходит MenuAction_End, в котором и происходит удаление хэндла, т.е все должно быть в порядке
Вы хотели сказать второй? В первом он даже не дойдёт до switch(action), если игрок не в той команде.
MenuAction_End происходит всегда при закрытии меню (Выбран пункт, Назад, Выход, перебило другое меню и т.д.)
 

Temlik

Участник
Сообщения
668
Реакции
174
Очепятка: hMenu.GetItem(iItem, "", 0_, szTitle, sizeof(szTitle)); (после 0 пропустил ,)
 

Nekro

Терра инкогнита
Сообщения
4,025
Реакции
2,260
А дополнительно надо как то удалять панель?
На сколько это вообще правильно?


C-подобный:
public void OnPluginStart()
{
    RegConsoleCmd("sm_panel", CmdList);
}

Action CmdList(int client, int args)
{
    Panel hPanel = CreatePanel();
    hPanel.SetTitle("Панель");
    hPanel.DrawText("Раз");
    hPanel.DrawText("Два");
    hPanel.DrawText("Нажмите 0 для выхода");
    hPanel.Send(client, PanelList, 20);
    return Plugin_Handled;
}

int PanelList(Menu hMenuLocal, MenuAction action, int client, int iItem)
{
    return 0;
}
 

Palonez

бб братки
Сообщения
3,035
Реакции
1,837
А дополнительно надо как то удалять панель?
На сколько это вообще правильно?


C-подобный:
public void OnPluginStart()
{
    RegConsoleCmd("sm_panel", CmdList);
}

Action CmdList(int client, int args)
{
    Panel hPanel = CreatePanel();
    hPanel.SetTitle("Панель");
    hPanel.DrawText("Раз");
    hPanel.DrawText("Два");
    hPanel.DrawText("Нажмите 0 для выхода");
    hPanel.Send(client, PanelList, 20);
    return Plugin_Handled;
}

int PanelList(Menu hMenuLocal, MenuAction action, int client, int iItem)
{
    return 0;
}
Кмк в случае с панелью есть вариант вообще не использовать кнопки, а просто показывать ее на время и чтобы избежать утечки, удалять хендл сразу при показе
Я так реализовываю
C++:
void OpenTopToMenu(int client, int type)
{
    char buffer[256];
    for(int i = 0; i < Rating.Length && i < sizeof(aadata); i++)
    {
        Rating.GetString(i, buffer, sizeof(buffer));
        Format(aadata[i], sizeof(aadata[]), "#%d. %s [%i]", i+1, buffer, Scores.Get(i));
        TrimString(aadata[i]);
    }

    Panel hPanel = CreatePanel(INVALID_HANDLE);
    switch(type)
    {
        case 2: Format(buffer, sizeof(buffer), "%t", "GhostTOP");   
        case 3: Format(buffer, sizeof(buffer), "%t", "CtsTOP");
    }
    hPanel.SetTitle(buffer);
    
    hPanel.DrawItem(" ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
    hPanel.DrawText("-----------------------------");
    hPanel.DrawItem(" ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
    
    int i = iMenuPos[client] * 10;
    int start = i;
    int end = i + 9;
    if(end > iMax) end = iMax;
    for(int k = i; k <= end; k++)
    {
        hPanel.DrawText(aadata[k]);
    }
    
    hPanel.DrawItem(" ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
    hPanel.DrawText("-----------------------------");
    hPanel.DrawItem(" ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);

    if(iMenuPos[client])
    {
        hPanel.CurrentKey = 6;
        FormatEx(buffer, sizeof(buffer), "<== (%d - %d)", start - 9, start);
        hPanel.DrawItem(buffer);
    }

    if(end < iMax-1)
    {
        i = end + 11;
        if(i > iMax) i = iMax;
        hPanel.CurrentKey = 7;
        FormatEx(buffer, sizeof(buffer), "==> (%d - %d)", end + 2, i);
        hPanel.DrawItem(buffer);
    }
    
    hPanel.DrawItem(" ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);   

    hPanel.CurrentKey = 8;
    hPanel.DrawItem("Назад");

    hPanel.CurrentKey = 9;
    hPanel.DrawItem("Выход");

    hPanel.Send(client, PanelHandler, 30);
    
    currenttype[client] = type;
    delete hPanel;
}

public int PanelHandler(Menu menu, MenuAction action, int client, int item)
{
    switch(item)
    {
        case 6:
        {
            iMenuPos[client]--;
            OpenTopToMenu(client, currenttype[client]);
        }
        case 7:
        {
            iMenuPos[client]++;
            OpenTopToMenu(client, currenttype[client]);
        }
        case 8:
        {
            iMenuPos[client] = 0;
            OpenRatingMenu(client);
        }
        case 9: iMenuPos[client] = 0;
    }
    return 0;
}
 
Сверху Снизу