<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.alliedmods.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kleiner</id>
	<title>AlliedModders Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.alliedmods.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kleiner"/>
	<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/Special:Contributions/Kleiner"/>
	<updated>2026-04-19T11:53:08Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.6</generator>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_Metamod:Source/ru&amp;diff=11219</id>
		<title>Installing Metamod:Source/ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_Metamod:Source/ru&amp;diff=11219"/>
		<updated>2021-07-20T14:09:49Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing Metamod:Source}}&lt;br /&gt;
&lt;br /&gt;
В этой статье вы узнаете, как установить [[Metamod:Source/ru|Metamod:Source]].&lt;br /&gt;
&lt;br /&gt;
=Обычная установка=&lt;br /&gt;
&lt;br /&gt;
Valve иногда вносит изменения в свои игры, которые нарушают SourceMod между выпусками.  Когда это происходит, вам может потребоваться установить новую версию Metamod и SourceMod.  Вы можете узнать, требуется ли это, на странице [[Required Versions (SourceMod)|необходимых версий]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;[http://www.metamodsource.net/ Скачайте] Metamod:Source.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Распакуйте архив в папку вашей игры.  Например, для Counter-Strike:Source после извлечения у вас будет &amp;lt;tt&amp;gt;cstrike/addons/metamod&amp;lt;/tt&amp;gt;.  Если вы загружаете файлы на FTP, распакуйте их локально, прежде чем переносить в папку игры вашего сервера.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Перезапустите свой сервер.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Введите &amp;quot;meta version&amp;quot; в консоли вашего сервера (или RCON).  Вы должны увидеть строку вида: &amp;quot;Loaded As: Valve Server Plugin&amp;quot;.  Если команда не распознана, обратитесь к разделам ниже.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Пользовательский VDF-файл==&lt;br /&gt;
Metamod:Source 1.10.0 и более поздние версии включают файл &amp;lt;tt&amp;gt;metamod.vdf&amp;lt;/tt&amp;gt; для облегчения установки на большинство игр. Если у вас возникли проблемы с его загрузкой, [http://www.metamodsource.net/?go=vdf перейдите сюда], чтобы получить VDF-файл, специфичный для вашей игры. Этот файл должен быть помещён в папку &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; вашего сервера.&lt;br /&gt;
&lt;br /&gt;
Известные установки, требующие выполнения этого шага:&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Left 4 Dead 1&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Моды сторонних разработчиков, использующие Source SDK Base.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Локальные серверы (созданные с помощью внутриигровой опции &amp;quot;Создать сервер&amp;quot;) для неанглоязычных клиентов игры.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=GameInfo=&lt;br /&gt;
'''Примечание: Обычно это не требуется - если вы не понимаете, что это такое, НЕ делайте этого, если нет инструкций. Приведённых выше инструкций достаточно для установки Metamod:Source на 99% серверов.'''&lt;br /&gt;
&lt;br /&gt;
Metamod:Source версии 1.4.2 и ниже использовал более старый метод загрузки.  Преимуществом этого метода было то, что Metamod:Source мог быть загружен до собственно игрового мода, что давало ему небольшую дополнительную функциональность.  Эта функциональность никогда не использовалась разработчиками плагинов, а обновления Steam продолжали перезаписывать файлы &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;, поэтому мы перешли на другой механизм загрузки.&lt;br /&gt;
&lt;br /&gt;
Однако этот механизм загрузки всё ещё может быть выбран, если вы столкнулись с проблемами обратной совместимости, или у вас есть плагин, который использует преимущества механизма ранней загрузки.  Если это ваш случай, то ниже приведены указания &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Откройте файл в папке мода под названием &amp;quot;gameinfo.txt&amp;quot;. Внизу вы увидите несколько строк следующего содержания:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SearchPaths&lt;br /&gt;
{&lt;br /&gt;
	Game				|gameinfo_path|. &lt;br /&gt;
	Game				cstrike&lt;br /&gt;
	Game				hl2&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Добавьте строку после знака &amp;quot;{&amp;quot;, но перед всеми записями &amp;quot;Game&amp;quot;, которая выглядит следующим образом:&amp;lt;pre&amp;gt;GameBin				|gameinfo_path|addons/metamod/bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Если вы используете Windows, вам может понадобиться использовать обратную косую черту (\).&lt;br /&gt;
&amp;lt;li&amp;gt;Всё готово! Чтобы проверить, сработало ли это, перезапустите ваш игровой сервер и введите &amp;quot;meta version&amp;quot; в консоли сервера.  Вы должны увидеть строку с надписью &amp;quot;Loaded as: GameDLL (gameinfo.txt)&amp;quot;.  Если команда не распознаётся, вероятно, установка не удалась.  Если в строке &amp;quot;Loaded as:&amp;quot; написано что-то другое, &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;, вероятно, был изменён неправильно.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Для получения дополнительной информации или документации см. [[:Category:Metamod:Source Documentation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Метод загрузки &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt; поддерживается только как унаследованная функция.  Инструмент patcher больше не предоставляется.  Вы можете пометить &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;, если хотите защитить его от перезаписи.&lt;br /&gt;
&lt;br /&gt;
Мы будем продолжать следить за тем, чтобы Metamod:Source мог загружаться этим методом до тех пор, пока движок Source позволяет это делать.  Однако мы будем больше концентрироваться на поддержке нового механизма загрузки для общего использования.&lt;br /&gt;
&lt;br /&gt;
[[Category:Metamod:Source Documentation]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_Metamod:Source/ru&amp;diff=11218</id>
		<title>Installing Metamod:Source/ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_Metamod:Source/ru&amp;diff=11218"/>
		<updated>2021-07-20T14:08:58Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Created page with &amp;quot;В этой статье вы узнаете, как установить Metamod:Source/ru.  =Обычная установка=  Valve иногда вносит изме...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;В этой статье вы узнаете, как установить [[Metamod:Source/ru]].&lt;br /&gt;
&lt;br /&gt;
=Обычная установка=&lt;br /&gt;
&lt;br /&gt;
Valve иногда вносит изменения в свои игры, которые нарушают SourceMod между выпусками.  Когда это происходит, вам может потребоваться установить новую версию Metamod и SourceMod.  Вы можете узнать, требуется ли это, на странице [[Required Versions (SourceMod)|необходимых версий]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;[http://www.metamodsource.net/ Скачайте] Metamod:Source.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Распакуйте архив в папку вашей игры.  Например, для Counter-Strike:Source после извлечения у вас будет &amp;lt;tt&amp;gt;cstrike/addons/metamod&amp;lt;/tt&amp;gt;.  Если вы загружаете файлы на FTP, распакуйте их локально, прежде чем переносить в папку игры вашего сервера.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Перезапустите свой сервер.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Введите &amp;quot;meta version&amp;quot; в консоли вашего сервера (или RCON).  Вы должны увидеть строку вида: &amp;quot;Loaded As: Valve Server Plugin&amp;quot;.  Если команда не распознана, обратитесь к разделам ниже.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Пользовательский VDF-файл==&lt;br /&gt;
Metamod:Source 1.10.0 и более поздние версии включают файл &amp;lt;tt&amp;gt;metamod.vdf&amp;lt;/tt&amp;gt; для облегчения установки на большинство игр. Если у вас возникли проблемы с его загрузкой, [http://www.metamodsource.net/?go=vdf перейдите сюда], чтобы получить VDF-файл, специфичный для вашей игры. Этот файл должен быть помещён в папку &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; вашего сервера.&lt;br /&gt;
&lt;br /&gt;
Известные установки, требующие выполнения этого шага:&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Left 4 Dead 1&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Моды сторонних разработчиков, использующие Source SDK Base.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Локальные серверы (созданные с помощью внутриигровой опции &amp;quot;Создать сервер&amp;quot;) для неанглоязычных клиентов игры.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=GameInfo=&lt;br /&gt;
'''Примечание: Обычно это не требуется - если вы не понимаете, что это такое, НЕ делайте этого, если нет инструкций. Приведённых выше инструкций достаточно для установки Metamod:Source на 99% серверов.'''&lt;br /&gt;
&lt;br /&gt;
Metamod:Source версии 1.4.2 и ниже использовал более старый метод загрузки.  Преимуществом этого метода было то, что Metamod:Source мог быть загружен до собственно игрового мода, что давало ему небольшую дополнительную функциональность.  Эта функциональность никогда не использовалась разработчиками плагинов, а обновления Steam продолжали перезаписывать файлы &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;, поэтому мы перешли на другой механизм загрузки.&lt;br /&gt;
&lt;br /&gt;
Однако этот механизм загрузки всё ещё может быть выбран, если вы столкнулись с проблемами обратной совместимости, или у вас есть плагин, который использует преимущества механизма ранней загрузки.  Если это ваш случай, то ниже приведены указания &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Откройте файл в папке мода под названием &amp;quot;gameinfo.txt&amp;quot;. Внизу вы увидите несколько строк следующего содержания:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SearchPaths&lt;br /&gt;
{&lt;br /&gt;
	Game				|gameinfo_path|. &lt;br /&gt;
	Game				cstrike&lt;br /&gt;
	Game				hl2&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Добавьте строку после знака &amp;quot;{&amp;quot;, но перед всеми записями &amp;quot;Game&amp;quot;, которая выглядит следующим образом:&amp;lt;pre&amp;gt;GameBin				|gameinfo_path|addons/metamod/bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Если вы используете Windows, вам может понадобиться использовать обратную косую черту (\).&lt;br /&gt;
&amp;lt;li&amp;gt;Всё готово! Чтобы проверить, сработало ли это, перезапустите ваш игровой сервер и введите &amp;quot;meta version&amp;quot; в консоли сервера.  Вы должны увидеть строку с надписью &amp;quot;Loaded as: GameDLL (gameinfo.txt)&amp;quot;.  Если команда не распознаётся, вероятно, установка не удалась.  Если в строке &amp;quot;Loaded as:&amp;quot; написано что-то другое, &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;, вероятно, был изменён неправильно.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Для получения дополнительной информации или документации см. [[:Category:Metamod:Source Documentation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Метод загрузки &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt; поддерживается только как унаследованная функция.  Инструмент patcher больше не предоставляется.  Вы можете пометить &amp;lt;tt&amp;gt;gameinfo.txt&amp;lt;/tt&amp;gt;, если хотите защитить его от перезаписи.&lt;br /&gt;
&lt;br /&gt;
Мы будем продолжать следить за тем, чтобы Metamod:Source мог загружаться этим методом до тех пор, пока движок Source позволяет это делать.  Однако мы будем больше концентрироваться на поддержке нового механизма загрузки для общего использования.&lt;br /&gt;
&lt;br /&gt;
[[Category:Metamod:Source Documentation]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/ru&amp;diff=11217</id>
		<title>Installing SourceMod/ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/ru&amp;diff=11217"/>
		<updated>2021-07-20T13:54:34Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
Установка SourceMod очень проста, и он может быть добавлен практически без изменений конфигурации.&lt;br /&gt;
&lt;br /&gt;
=Требования=&lt;br /&gt;
Веб-браузер с графическим интерфейсом для получения сжатых архивов Metamod и SourceMod.&lt;br /&gt;
Инструмент для копирования архива на ваш выделенный сервер.&lt;br /&gt;
&lt;br /&gt;
SourceMod требует [[Metamod:Source/ru|Metamod:Source]] 1.9.0 или выше (рекомендуется использовать последнюю версию). [http://www.metamodsource.net/ Нажмите здесь], чтобы посетить главную страницу Metamod:Source. Инструкции по установке SourceMM вручную можно найти [[Installing Metamod:Source/ru|здесь]].&lt;br /&gt;
&lt;br /&gt;
SourceMod будет работать практически с любым модом, созданным с использованием Source SDK.&lt;br /&gt;
&lt;br /&gt;
Valve иногда вносит изменения в свои игры, которые нарушают SourceMod между выпусками. &lt;br /&gt;
 Когда это происходит, вам может потребоваться установить новую версию Metamod и SourceMod.  Вы можете узнать, требуется ли это, на странице [[Required Versions (SourceMod)/ru|необходимых версий]].&lt;br /&gt;
&lt;br /&gt;
=Загрузка/установка=&lt;br /&gt;
==Локальный сервер==&lt;br /&gt;
Чтобы установить [http://www.sourcemod.net/downloads.php SourceMod] в локальное хранилище, просто извлеките архив &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) или &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) в папку вашего мода (например, &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; для Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; для Day of Defeat и так далее).&lt;br /&gt;
&lt;br /&gt;
==Выделенный сервер==&lt;br /&gt;
Чтобы установить SourceMod на выделенный сервер, сначала распакуйте архив &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) или &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) на локальный компьютер (например, на рабочий стол).  Вы увидите папку &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Используя такой инструмент, как &lt;br /&gt;
[https://encrypted.google.com/#q=FTP FTP], найдите папку вашего мода (например, &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; для Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; для Day of Defeat:Source и так далее).  Под этой папкой должна находиться папка &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; (если это не так, Metamod:Source, вероятно, не установлен).  Из локальной папки &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; загрузите всё содержимое в папку выделенного сервера &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;.  После этого в папке выделенного сервера &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; должна появиться папка &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если у вас возникли трудности с этими шагами, вам необходимо ознакомиться с FTP и управлением сервером.  Однако вы также можете обратиться за помощью к своему провайдеру сервера.  Некоторые провайдеры также имеют веб-интерфейсы для управления вашим сервером.&lt;br /&gt;
&lt;br /&gt;
В качестве альтернативы, если вы скопировали tar.gz в папку srcds, выполните следующие действия из подпапки cstrike:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Проверка установки=&lt;br /&gt;
Расположение папок должно выглядеть следующим образом:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[мод]&amp;lt;/tt&amp;gt; - Папка вашего мода&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
После загрузки/копирования SourceMod и настройки Metamod:Source полностью перезапустите ваш сервер.  Если он локальный, выключите его и перезапустите.  Если сервер выделенный, вам может понадобиться обратиться за помощью к провайдеру.  Тем не менее, часто безопасно отдать команду &amp;quot;quit&amp;quot; через [[rcon]], поскольку большинство провайдеров автоматически перезапустят ваш сервер.&lt;br /&gt;
&lt;br /&gt;
Сначала в [[server console/ru|консоль сервера]] (не клиентской консоли) введите:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если установка прошла успешно, вы увидите что-то вроде:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Найден 1 плагин:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) от AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После этого вы должны иметь возможность использовать корневую команду консоли SourceMod, которая вызывается просто:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 Информация о версии SourceMod:&lt;br /&gt;
    SourceMod версии: 1.1.0.2489&lt;br /&gt;
    Движок SourcePawn: SourcePawn 1.1, jit-x86 (сборка 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: вер.1 = 4, вер.2 = 2&lt;br /&gt;
    Составлено на: 5 сентября 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Наконец, если вы уже настроили административного пользователя (если нет, то прочтите [[Adding Admins (SourceMod)/ru|эту страницу]]), вы можете протестировать игровое меню, присоединившись к серверу, и в клиентской консоли наберите следующее:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
Должно появиться всплывающее меню со всеми опциями.&lt;br /&gt;
&lt;br /&gt;
=Устранение проблем=&lt;br /&gt;
Если установка не удалась, обычно вы увидите одну из четырёх причин.  &lt;br /&gt;
&lt;br /&gt;
==Metamod сообщает NOFILE или FAILED==&lt;br /&gt;
Если &amp;quot;meta list&amp;quot; ответит что-то вроде:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Найден 1 плагин:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скорее всего, либо файлы расположены в неправильном месте, либо файл не удалось загрузить.  Для получения дополнительной информации используйте следующую команду (за исключением использования правильного номера списка):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==В списке Metamod нет плагинов==&lt;br /&gt;
Если &amp;quot;meta list&amp;quot; ответит что-то вроде:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Найдено 0 плагинов:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Существует несколько причин этой ошибки.&lt;br /&gt;
&lt;br /&gt;
# Наиболее распространённой причиной является то, что файл sourcemod.vdf не может быть расположен в папке addons/metamod.  Убедитесь, что файл sourcemod.vdf присутствует в этой папке.&lt;br /&gt;
# Если файл sourcemod.vdf присутствует, убедитесь, что вы используете правильную сборку Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod ничего не пишет==&lt;br /&gt;
Если &amp;quot;meta list&amp;quot; вообще не имеет ответа, значит Metamod:Source установлен неправильно. [[Installing Metamod:Source/ru|Эта страница вики]] может подсказать вам, как решить эту проблему.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11216</id>
		<title>Menu API (SourceMod)=ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11216"/>
		<updated>2021-07-20T13:48:00Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Redirected page to Menu API (SourceMod)/ru&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Menu API (SourceMod)/ru]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Ru:Menu_API_(SourceMod)&amp;diff=11215</id>
		<title>Ru:Menu API (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Ru:Menu_API_(SourceMod)&amp;diff=11215"/>
		<updated>2021-07-20T13:47:59Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Redirected page to Menu API (SourceMod)/ru&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Menu API (SourceMod)/ru]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)/ru&amp;diff=11214</id>
		<title>Menu API (SourceMod)/ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)/ru&amp;diff=11214"/>
		<updated>2021-07-20T13:47:58Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Removed redirect to Ru:Menu API (SourceMod)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Menu API (SourceMod)}}&lt;br /&gt;
SourceMod обладает большим API для создания и отображений меню для клиентов. В отличии от AMX Mod X, API SourceMod'a обладает широким функционалом. Меню базируются на обратных вызовах (callbacks), которые гарантированно будут вызваны.&lt;br /&gt;
&lt;br /&gt;
Для C++, API меню может быть найдено в &amp;lt;tt&amp;gt;public/IMenuManager.h&amp;lt;/tt&amp;gt;. Для SourcePawn - &amp;lt;tt&amp;gt;scripting/include/menus.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Объекты=&lt;br /&gt;
Система меню SourceMod основана на объектно ориентированной иерархии. Понимание этой иерархии, даже для скриптинга, является ключевым моментом для эффективного использования меню.&lt;br /&gt;
&lt;br /&gt;
==Стили==&lt;br /&gt;
Высший уровень объектов называется ''MenuStyle'' (&amp;lt;tt&amp;gt;IMenuStyle&amp;lt;/tt&amp;gt; в C++). Стили описывают уникальную систему меню. Всего есть два стиля, встроенных в SourceMod:&lt;br /&gt;
*Valve Styly, так же называемым &amp;quot;ESC&amp;quot; меню; 8 пунктов на странице, нельзя добавить текст без занятия слота или спрятать (отключить) текст.&lt;br /&gt;
*Radio Style, так же называемый &amp;quot;AMX&amp;quot; меню; 10 пунктов на странице, можно добавить любой текст.&lt;br /&gt;
&lt;br /&gt;
Каждый MenuStyle обладает своими правилами и свойствами. Вы можете считать, что каждый из них существует на раздельных &amp;quot;каналах&amp;quot;. Например, два разных меню могут быть на экране игрока, будучи Valve и Radio меню одновременно, и SourceMod сможет обработать их обоих без всяких проблем. Это всё возможно благодаря тому, что каждый стиль отслеживает свои меню отдельно.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Меню низшего уровня называется ''Панелью'' (&amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; в C++). Панель - один ''''кусок'''' текста. Пункты и текст могут добавляться к панели столько раз, сколько поддерживает родительский стиль. Например, Valve стиль не поддерживает отображение текста без занятия слота или отключенных пунктов. Однако с Radio стилем панели это становится возможным, и вы можете отобразить большое количество текста так, как вам это захочется. &lt;br /&gt;
&lt;br /&gt;
Панель считается временным объектом. Она создается, рендерится, отображается, и затем удаляется. Хотя она может быть сохранена на неопределенное время, если в этом есть необходимость.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Vavle стиля:&lt;br /&gt;
*Максимум пунктов на странице: 8&lt;br /&gt;
*Отключенный текст не отображается.&lt;br /&gt;
*Нельзя написать текст, не заняв слота.&lt;br /&gt;
*Пробелы не добавляются к пробелам\новым линиям, добавляя чувство &amp;quot;ограниченности&amp;quot;. &lt;br /&gt;
*Пользователь должен нажать &amp;quot;ESC&amp;quot; или быть в своей консоли, чтобы увидеть меню.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Radio стиля:&lt;br /&gt;
*10 пунктов на странице&lt;br /&gt;
*Заглавия белые; пункты желтые, если не отключены. Если отключены, то белого цвета.&lt;br /&gt;
*Нулевой элемент всегда белый. Для согласованности это значит, что навигационное управление всегда белое и находится в следующей секции, и просто не отрисовывается, если отсутствует.&lt;br /&gt;
&lt;br /&gt;
==Меню==&lt;br /&gt;
Наконец, есть просто ''Меню'' (&amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; in C++). Это вспомогательный объект, созданный для хранения базированных на меню выбираемых пунктов. В отличии от низкоуровневых панелей, меню содержит '''пункты''', и может содержать только те пункты, которые можно выбрать (т.е. занимающие слоты). Они делятся на две категории:&lt;br /&gt;
*Не нумерованные: эти меню могут иметь определенное число пунктов, и не имеют навигационных опций, кроме кнопки &amp;quot;Выход&amp;quot;, которая всегда добавляется на последнюю позицию, поддерживаемую стилем.&lt;br /&gt;
**Valve Стиль: максимум пунктов: 8&lt;br /&gt;
**Radio Стиль: максимум пунктов: 10&lt;br /&gt;
*Нумерованные: такие меню могут содержать сколь угодно пунктов. Во время отображения будет показано только определенное число пунктов. Автоматически будут добавлены навигационные пункты, таким образом, игроки легко могут возвращаться назад и вперед к определенным &amp;quot;страницам&amp;quot; пунктов меню.&lt;br /&gt;
**&amp;quot;Назад&amp;quot; всегда отображается как первый навигационный пункт, третий с конца поддерживаемых позиций. Не будет добавлен, если меню содержит только одну страницу. Если нет никаких предыдущих страниц, текст никогда не будет отображен, ни в каком стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 6&lt;br /&gt;
***Radio Стиль, позиция: 8&lt;br /&gt;
**&amp;quot;Вперед&amp;quot; всегда отображается как второй навигационный пункт, второй с конца из доступных позиций. Не отображается, если меню состоит из одной страницы. Если нет следующих страниц, то текс не будет отображен при любом стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 7&lt;br /&gt;
***Radio Стиль, позиция: 9&lt;br /&gt;
**&amp;quot;Выход&amp;quot; отображается, если у меню включена кнопка &amp;quot;выход&amp;quot;. Всегда занимает последнюю из возможных позиций&lt;br /&gt;
***Valve Стиль, позиция: 8&lt;br /&gt;
***Radio Стиль, позиция: 10&lt;br /&gt;
&lt;br /&gt;
Цель меню - упростить процедуру хранения, отображения, и вычисления выбранного пункта. Таким образом, меню не позволяет добавлять пустой текст, так как это значительно усложняет алгоритм отображения.  &lt;br /&gt;
&lt;br /&gt;
Внутренне, меню отображается через ''RenderMenu'' алогритм. Этот алгоритм создает временную панель и заполняет её пунктами из меню. Эта панель отображается клиенту. Алгоритм пытается создать свободное перемещение по всем меню, и по всем стилям. Таким образом, все меню отображаются через &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; класс, или &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; дескрипторы (Handles), и будет выглядеть и действовать так же, как и меню API, основанное на панельном API.&lt;br /&gt;
&lt;br /&gt;
=Обратные вызовы (callbacks)=&lt;br /&gt;
==Краткий обзор==&lt;br /&gt;
Меню базируются на системе обратных вызовов. Каждый обратный вызов предоставляет собой действие, которые происходят во время ''цикла отображения меню''. Цикл состоит из множества уведомлений:&lt;br /&gt;
*Начало уведомления.&lt;br /&gt;
**Уведомление об отображении, если меню может быть отображено клиенту.&lt;br /&gt;
**Уведомление о выборе пункта или кнопки отмены.&lt;br /&gt;
*Конец уведомления.&lt;br /&gt;
&lt;br /&gt;
Так как ''End'' обозначает конец полного цикла отображения, то обычно его используют для уничтожения временных меню.&lt;br /&gt;
&lt;br /&gt;
==Спецификация==&lt;br /&gt;
Детальное объяснение этих событий ниже. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель всегда доступен. Для SourcePawn, &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; хандл и &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; всегда установлены в &amp;lt;tt&amp;gt;MenuHandler&amp;lt;/tt&amp;gt; callback. В отличии от C++, SourcePawn API позволяет вызвать лишь некоторые действия, если они были запрошены во время создания меню. Это сделано в целях оптимизации. Однако, эти действия не могут быть не вызваны.&lt;br /&gt;
&lt;br /&gt;
*'''Start'''. Меню было признано. Это не означает, что меню будет отображено; однако, это гарантирует, что событие &amp;quot;OnMenuEnd&amp;quot; будет вызвано.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuStart()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
*'''Display'''.  Меню отобразилось клиенту.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuDisplay()&amp;lt;/tt&amp;gt; в C++. &amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; доступны указатель и индекс клиента.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Display&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Хандл меню.&lt;br /&gt;
*'''Select'''. Пункт был выбран. Будет дана позиция пункта в меню, а не нажатая кнопка (если меню не является панелью) &lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и позиция пункта.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Позиция пункта.&lt;br /&gt;
*'''Cancel'''.  Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
*'''End'''. Цикл отображения меню закончился; это означает, что &amp;quot;Start&amp;quot; действие было, и одно из действий &amp;quot;Select&amp;quot; или &amp;quot;Cancel&amp;quot; так же произошло. Обычно в этом месте отчищаются ресурсы меню или само меню удаляется.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuEnd()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Причина завершения меню.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: если param1 == MenuEnd_Cancelled, то здесь содержится код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Для панелей цикл жизни другой. Панели могут вызывать лишь два callback'а из написанных выше, и гарантируется, что один из них точно будет вызван для данного цикла отображения. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель будет всегда &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. Для SourcePawn, меню Handle будет всегда &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*'''Select'''.  Кнопка была нажата. Этой кнопкой может быть любое число. Например, если в панели у вас 2 пункта, то клиент может вызвать это событие, нажав &amp;quot;43&amp;quot;.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и нажатая кнопка.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Номер нажатой клавиши.&lt;br /&gt;
*'''Cancel'''. Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Примеры=&lt;br /&gt;
Для начала попробуем сделать очень простое меню:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы любите яблоки?&lt;br /&gt;
1. Да&lt;br /&gt;
2. Нет&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделаем это как меню и как панель, чтобы увидеть различия в их API.&lt;br /&gt;
&lt;br /&gt;
==Простое меню==&lt;br /&gt;
Сперва напишем наш пример через API Меню. Для пошагового руководства смотрите [[Menus Step By Step (SourceMod Scripting)]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду menu_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// хандл меню&lt;br /&gt;
int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
	/* Если был выбран какой-либо пункт, то сообщим клиенту о его выборе. */&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		char info[32]; // переменная для хранения выбора&lt;br /&gt;
		bool found = GetMenuItem(menu, param2, info, sizeof(info)); // получаем информацию о выбранном в меню пункте&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы нажали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info); // пишем клиенту в консоль&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню было отменено, то сообщим об этом серверу. */&lt;br /&gt;
	else if (action == MenuAction_Cancel)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл меню.  Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню &amp;quot;закончилось&amp;quot;, то удалим его из памяти */&lt;br /&gt;
	else if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		delete menu;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:menu = CreateMenu(MenuHandler1);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;да&amp;quot;, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;нет&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	DisplayMenu(menu, client, 20);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несколько очень важных замечаний о этом примере:&lt;br /&gt;
*Одно из &amp;lt;tt&amp;gt;Select&amp;lt;/tt&amp;gt; или &amp;lt;tt&amp;gt;Cancel&amp;lt;/tt&amp;gt; событий всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*&amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*Мы удаляем наше меню в &amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; действии, потому что наш дескриптор (Handle) нам больше не нужен. Если бы мы удалили его после &amp;lt;tt&amp;gt;DisplayMenu&amp;lt;/tt&amp;gt;, это бы отменило отображение меню клиенту.&lt;br /&gt;
*Меню по умолчанию имеет кнопку выхода. В нашем примере мы его отключили.&lt;br /&gt;
*Наше меню будет отображаться 20 секунд. Это означает, что если клиент ничего не выбрал в течении 20 секунд, то меню будет закрыто. Это обычно необходимо для меню с голосованиями. В отличии от AMX Mod X, вам не нужно создавать таймер, чтобы быть увереным, что меню &amp;quot;закончилось&amp;quot;.&lt;br /&gt;
*Мы создали и уничтожили новый дескриптор меню (Menu Handle), однако мы можем этого и не делать. Вполне допустимо создать дескриптор (Handle) один раз для всего времени жизни плагина.&lt;br /&gt;
*Чтобы нормально отображались русские буквы, кодировка файла должна быть UTF-8 (без BOOM), иначе будут квадратики вместо русских букв.&lt;br /&gt;
&lt;br /&gt;
Наше законченное меню и приложенный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика).&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовая панель==&lt;br /&gt;
Теперь давайте перепишем наш пример с использованием Панели вместо Меню.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду panel_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;panel_test1&amp;quot;, Panel_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public PanelHandler1(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d&amp;quot;, param2);&lt;br /&gt;
	} else if (action == MenuAction_Cancel) {&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл панель. Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Panel_Test1(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:panel = CreatePanel();&lt;br /&gt;
	SetPanelTitle(panel, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Да&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Нет&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
	SendPanelToClient(panel, client, PanelHandler1, 20);&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(panel);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как вы можете заметить, Панели сильно отличаются от Меню:&lt;br /&gt;
*Мы можем удалить Панель как только закончим его отображать. Мы можем создать Панель один раз, и продолжать использовать его много раз, однако мы можем удалить его в любой момент, без нарушения цикла отображения клиентского меню.&lt;br /&gt;
*Обработчик Панели (Handler) получает гораздо меньше информации. Так как Панели разрабатывались как грубый вывод текста, была сохранена возможность добавления информации без занятия слота. Поэтому обработчик может знать лишь об отмене или о нажатии любой клавиши с номером.&lt;br /&gt;
*Отсутствует автоматизация. Вы не можете добавить больше определенного числа выбираемых пунктов и добавить нумерацию. Автоматический контроль требует использования тяжелого объектного API Меню.&lt;br /&gt;
&lt;br /&gt;
Наше законченная панель и приложеный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_panel_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовое меню с нумерацией==&lt;br /&gt;
Теперь сделаем пример с большим функционалом -- нумерацией. Попробуем сделать меню для смены карты. Самый простой путь - прочесть &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл в начале плагина и создать меню по этому файла.&lt;br /&gt;
&lt;br /&gt;
Так как чтение и парсинг файла довольно долгая операция, мы будем делать это только раз за карту. Поэтому мы построим меню в &amp;lt;tt&amp;gt;OnMapStart&amp;lt;/tt&amp;gt;, и мы не будем вызывать &amp;lt;tt&amp;gt;CloseHandle&amp;lt;/tt&amp;gt; до события &amp;lt;tt&amp;gt;OnMapEnd&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:g_MapMenu = INVALID_HANDLE&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_changemap&amp;quot;, Command_ChangeMap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	g_MapMenu = BuildMapMenu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapEnd()&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu != INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		CloseHandle(g_MapMenu);&lt;br /&gt;
		g_MapMenu = INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Handle:BuildMapMenu()&lt;br /&gt;
{&lt;br /&gt;
	/* открываем файл */&lt;br /&gt;
	new Handle:file = OpenFile(&amp;quot;maplist.txt&amp;quot;, &amp;quot;rt&amp;quot;);&lt;br /&gt;
	if (file == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	/* создаем хандл меню */&lt;br /&gt;
	new Handle:menu = CreateMenu(Menu_ChangeMap);&lt;br /&gt;
	new String:mapname[255];&lt;br /&gt;
	while (!IsEndOfFile(file) &amp;amp;&amp;amp; ReadFileLine(file, mapname, sizeof(mapname)))&lt;br /&gt;
	{&lt;br /&gt;
		if (mapname[0] == ';' || !IsCharAlpha(mapname[0]))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* выбрасываем все пробелы из названия */&lt;br /&gt;
		new len = strlen(mapname);&lt;br /&gt;
		for (new i=0; i&amp;lt;len; i++)&lt;br /&gt;
		{&lt;br /&gt;
			if (IsCharSpace(mapname[i]))&lt;br /&gt;
			{&lt;br /&gt;
				mapname[i] = '\0';&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		/* проверяем карту на валидность */&lt;br /&gt;
		if (!IsMapValid(mapname))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* добавляем карту в меню */&lt;br /&gt;
		AddMenuItem(menu, mapname, mapname);&lt;br /&gt;
	}&lt;br /&gt;
	/* не забываем закрыть файл! */&lt;br /&gt;
	CloseHandle(file);&lt;br /&gt;
	&lt;br /&gt;
	/* наконец, устанавливаем заглавие меню */&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Выберите карту:&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return menu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Menu_ChangeMap(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		new String:info[32];&lt;br /&gt;
&lt;br /&gt;
		/* получаем информацию о пункте */&lt;br /&gt;
		new bool:found = GetMenuItem(menu, param2, info, sizeof(info));&lt;br /&gt;
&lt;br /&gt;
		/* говорим об этом клиенту */&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
&lt;br /&gt;
		/* меняем карту */&lt;br /&gt;
		ServerCommand(&amp;quot;changelevel %s&amp;quot;, info);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_ChangeMap(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Файл maplist.txt не был найден!&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}	&lt;br /&gt;
	&lt;br /&gt;
	DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результатом такого меню будет множество пунктов (&amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл содержал примерно 18 карт). Таким образом, конечное меню содержит три страницы, которые идут друг за другом, и выглядят вот так:&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_2_page1.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page2.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page3.PNG]]&lt;br /&gt;
&lt;br /&gt;
Сообщение в консоль будет выведено до смены карты, например, если выбрали &amp;lt;tt&amp;gt;cs_office&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы выбрали: 8 (найдено? 1 информация: cs_office)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отображение и разработка этого меню через &amp;lt;tt&amp;gt;ShowMenu&amp;lt;/tt&amp;gt; сообщение или &amp;lt;tt&amp;gt;Панельный&amp;lt;/tt&amp;gt; API занимает много времени и является довольно сложной задачей. Придется отслеживать все пункты в массиве большого размера, страницы, которые пользователь просматривает, и написать функцию, которая будет вычислять, на основе текущей странице, пункт, который был выбран и какую кнопку нажали. Меню системы это всё делает за вас.&lt;br /&gt;
&lt;br /&gt;
'''Заметки:'''&lt;br /&gt;
*Пункты управления, которые не доступны, не рисуются. Например, на первой странице вы не можете нажать &amp;quot;назад&amp;quot;, а на последней странице вы не можете нажать &amp;quot;вперед&amp;quot;. Несмотря на это, меню пытается удерживать каждый интерфейс последовательно. Таким образом, визуально, каждая навигационная клавиша всегда на одной и той же позиции.&lt;br /&gt;
*Если указать таймаут меню, то переключение между страницами не воздействует на общее время открытия меню. Например, если мы установим таймаут на 20 секунд, то каждое листание страницы будет устанавливать таймайт равный (20 - время, уже затраченное на просмотр). Только переоткрытие меню позволит вернуть таймаут времени на 20 секунд.&lt;br /&gt;
*Если отключить кнопку выхода, то не смотря на это, пункты 8 и 9 будут всё так же &amp;quot;назад&amp;quot; и вперед, соответственно.&lt;br /&gt;
*Мы не освобождаем дескриптор (Handle) меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, потому что наше меню глобальное/статическое, и нам не нужно пересоздавать его каждый раз.&lt;br /&gt;
*На изображении отображена кнопка &amp;quot;Назад&amp;quot;. В SourceMod версии 1011 и выше, кнопка &amp;quot;Назад&amp;quot; была переименована в &amp;quot;Предыдующая&amp;quot;, и &amp;quot;Назад&amp;quot; зарезервированно для &amp;quot;ExitBack&amp;quot; функциональности. (актуально для ENG версии, на ру вроде бы оба пункта &amp;quot;назад&amp;quot;. прим. переводчик)&lt;br /&gt;
&lt;br /&gt;
=Голосование=&lt;br /&gt;
SourceMod также имеет API для отображения меню как голосования для больше чем одного клиента. SourceMod автоматически обрабатываем выбранные пункты and randomly picking a tie-breaker (не знаю как перевести. прим. переводчика). Так же API голосования добавляет два новых значения для &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt;, которые во время отображения меню всегда вызываются.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteStart&amp;lt;/tt&amp;gt;: Вызывается после &amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt;, когда голосование оффициально началось.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;: Вызывается после того, как все клиенты проголосовали или отменили свои меню голосований. Выбранный предмет может быть получен через &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. Вызывается '''перед''' &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. Важно заметить, что это не замена &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, это разные вещи. Нельзя освобождать меню в &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;. '''Заметка:''' не вызывается, если используется &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteCancel&amp;lt;/tt&amp;gt;: Вызывается, если меню было отмеено в процессе голосования. Если вызвалось, то &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; или обратный вызов (callback) для результата голосований не будет вызваны, но &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; будет после этого.  Причина отмены голосования находится в &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Система меню - это то же самое меню, только с двумя дополнительными свойствами:&lt;br /&gt;
*Только одно голосование может быть активно. Вы должны проверять, не запущено ли голосование (&amp;lt;tt&amp;gt;IsVoteInProgress&amp;lt;/tt&amp;gt;()), иначе &amp;lt;tt&amp;gt;VoteMenu&amp;lt;/tt&amp;gt;() не сработает.&lt;br /&gt;
*Если клиент во время голосования отключится, то его голос будет недействительным. &lt;br /&gt;
&lt;br /&gt;
Пример внизу показывает, как создать функцию &amp;lt;tt&amp;gt;DoVoteMenu()&amp;lt;/tt&amp;gt;, которая будет спрашивать, хотят ли они сменить карту на предложенную.&lt;br /&gt;
&lt;br /&gt;
==Простое Голосование==&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Продвинутое Голосование==&lt;br /&gt;
Если вам нужно больше информации о результатах голосовании, чем вам дает &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;, вы можете указать отдельный обратный вызов (callback). Новый обратный вызов будет давать больше информации, но за опреденную цену: &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; не будет вызвано, и вам придется самим решать, как обработать результат. Это делается с помощью &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Handle_VoteResults(Handle:menu, &lt;br /&gt;
			num_votes, &lt;br /&gt;
			num_clients, &lt;br /&gt;
			const client_info[][2], &lt;br /&gt;
			num_items, &lt;br /&gt;
			const item_info[][2])&lt;br /&gt;
{&lt;br /&gt;
	/* Проверяем, нет ли сразу двух победителей */&lt;br /&gt;
	new winner = 0;&lt;br /&gt;
	if (num_items &amp;gt; 1&lt;br /&gt;
	    &amp;amp;&amp;amp; (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))&lt;br /&gt;
	{&lt;br /&gt;
		winner = GetRandomInt(0, 1);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	new String:map[64];&lt;br /&gt;
	GetMenuItem(menu, item_info[winner][VOTEINFO_ITEM_INDEX], map, sizeof(map));&lt;br /&gt;
	ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetVoteResultCallback(menu, Handle_VoteResults);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=ExitBack=&lt;br /&gt;
ExitBack - специальный термин для обозначения &amp;quot;ExitBack Кнопки&amp;quot;. По умолчанию эта кнопка отключена.  Обычно нумерованные меню не имеют пункта &amp;quot;Назад&amp;quot; для первой страници. Если &amp;quot;ExitBack&amp;quot; кнопка включена, появится кнопка &amp;quot;Назад&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
Выбор опции &amp;quot;ExitBack&amp;quot; для меню в обратном вызове будет в действии &amp;lt;tt&amp;gt;MenuCancel_ExitBack&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;MenuEnd_ExitBack&amp;lt;/tt&amp;gt;. Функциональность этого такая же, как и у простого выхода из меню; дополнительная функциональность должна быть определена через обратный вызов.&lt;br /&gt;
&lt;br /&gt;
=Освобождение Дескрипторов (Handle) меню=&lt;br /&gt;
Важно закрывать дескриптор меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; вызывается когда меню закрылось и больше не нужно.&lt;br /&gt;
&lt;br /&gt;
=Переводы=&lt;br /&gt;
Есть возможность динамически переводить меню для каждого игрока через &amp;lt;tt&amp;gt;MenuAction_DisplayItem&amp;lt;/tt&amp;gt; callback. Специальный натив, &amp;lt;tt&amp;gt;RedrawMenuItem&amp;lt;/tt&amp;gt;, используется чтобы преобразовать текст внутри вызова. Давайте переделаем наш пример голосования с переводами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	} else if (action == MenuAction_DisplayItem) {&lt;br /&gt;
		/* Получаем отображаемую строку, будем использовать как фразу перевода */&lt;br /&gt;
		decl String:display[64];&lt;br /&gt;
		GetMenuItem(menu, param2, &amp;quot;&amp;quot;, 0, _, display, sizeof(display));&lt;br /&gt;
&lt;br /&gt;
		/* Переводим строку на язык клиента */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, display, param1);&lt;br /&gt;
&lt;br /&gt;
		/* Перерисовываем текст */&lt;br /&gt;
		return RedrawMenuItem(buffer);&lt;br /&gt;
	} else if (action == MenuAction_Display) {&lt;br /&gt;
		/* Дескриптор панели будет вторым параметром */&lt;br /&gt;
		new Handle:panel = Handle:param2;&lt;br /&gt;
		&lt;br /&gt;
		/* Получаем название карты, на которую меняем, из первого пункта */&lt;br /&gt;
		decl String:map[64];&lt;br /&gt;
		GetMenuItem(menu, 0, map, sizeof(map));&lt;br /&gt;
		&lt;br /&gt;
		/* Переводим нашу фразу */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Change map to?&amp;quot;, client, map);&lt;br /&gt;
&lt;br /&gt;
		SetPanelTitle(panel, buffer);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu, MenuAction_DisplayItem|MenuAction_Display);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Ru:Menu_API_(SourceMod)&amp;diff=11213</id>
		<title>Ru:Menu API (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Ru:Menu_API_(SourceMod)&amp;diff=11213"/>
		<updated>2021-07-20T13:42:22Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11211</id>
		<title>Menu API (SourceMod)=ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11211"/>
		<updated>2021-07-20T13:41:00Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Kleiner moved page Ru:Menu API (SourceMod) to Menu API (SourceMod)=ru: testing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Menu API (SourceMod)}}&lt;br /&gt;
SourceMod обладает большим API для создания и отображений меню для клиентов. В отличии от AMX Mod X, API SourceMod'a обладает широким функционалом. Меню базируются на обратных вызовах (callbacks), которые гарантированно будут вызваны.&lt;br /&gt;
&lt;br /&gt;
Для C++, API меню может быть найдено в &amp;lt;tt&amp;gt;public/IMenuManager.h&amp;lt;/tt&amp;gt;. Для SourcePawn - &amp;lt;tt&amp;gt;scripting/include/menus.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Объекты=&lt;br /&gt;
Система меню SourceMod основана на объектно ориентированной иерархии. Понимание этой иерархии, даже для скриптинга, является ключевым моментом для эффективного использования меню.&lt;br /&gt;
&lt;br /&gt;
==Стили==&lt;br /&gt;
Высший уровень объектов называется ''MenuStyle'' (&amp;lt;tt&amp;gt;IMenuStyle&amp;lt;/tt&amp;gt; в C++). Стили описывают уникальную систему меню. Всего есть два стиля, встроенных в SourceMod:&lt;br /&gt;
*Valve Styly, так же называемым &amp;quot;ESC&amp;quot; меню; 8 пунктов на странице, нельзя добавить текст без занятия слота или спрятать (отключить) текст.&lt;br /&gt;
*Radio Style, так же называемый &amp;quot;AMX&amp;quot; меню; 10 пунктов на странице, можно добавить любой текст.&lt;br /&gt;
&lt;br /&gt;
Каждый MenuStyle обладает своими правилами и свойствами. Вы можете считать, что каждый из них существует на раздельных &amp;quot;каналах&amp;quot;. Например, два разных меню могут быть на экране игрока, будучи Valve и Radio меню одновременно, и SourceMod сможет обработать их обоих без всяких проблем. Это всё возможно благодаря тому, что каждый стиль отслеживает свои меню отдельно.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Меню низшего уровня называется ''Панелью'' (&amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; в C++). Панель - один ''''кусок'''' текста. Пункты и текст могут добавляться к панели столько раз, сколько поддерживает родительский стиль. Например, Valve стиль не поддерживает отображение текста без занятия слота или отключенных пунктов. Однако с Radio стилем панели это становится возможным, и вы можете отобразить большое количество текста так, как вам это захочется. &lt;br /&gt;
&lt;br /&gt;
Панель считается временным объектом. Она создается, рендерится, отображается, и затем удаляется. Хотя она может быть сохранена на неопределенное время, если в этом есть необходимость.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Vavle стиля:&lt;br /&gt;
*Максимум пунктов на странице: 8&lt;br /&gt;
*Отключенный текст не отображается.&lt;br /&gt;
*Нельзя написать текст, не заняв слота.&lt;br /&gt;
*Пробелы не добавляются к пробелам\новым линиям, добавляя чувство &amp;quot;ограниченности&amp;quot;. &lt;br /&gt;
*Пользователь должен нажать &amp;quot;ESC&amp;quot; или быть в своей консоли, чтобы увидеть меню.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Radio стиля:&lt;br /&gt;
*10 пунктов на странице&lt;br /&gt;
*Заглавия белые; пункты желтые, если не отключены. Если отключены, то белого цвета.&lt;br /&gt;
*Нулевой элемент всегда белый. Для согласованности это значит, что навигационное управление всегда белое и находится в следующей секции, и просто не отрисовывается, если отсутствует.&lt;br /&gt;
&lt;br /&gt;
==Меню==&lt;br /&gt;
Наконец, есть просто ''Меню'' (&amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; in C++). Это вспомогательный объект, созданный для хранения базированных на меню выбираемых пунктов. В отличии от низкоуровневых панелей, меню содержит '''пункты''', и может содержать только те пункты, которые можно выбрать (т.е. занимающие слоты). Они делятся на две категории:&lt;br /&gt;
*Не нумерованные: эти меню могут иметь определенное число пунктов, и не имеют навигационных опций, кроме кнопки &amp;quot;Выход&amp;quot;, которая всегда добавляется на последнюю позицию, поддерживаемую стилем.&lt;br /&gt;
**Valve Стиль: максимум пунктов: 8&lt;br /&gt;
**Radio Стиль: максимум пунктов: 10&lt;br /&gt;
*Нумерованные: такие меню могут содержать сколь угодно пунктов. Во время отображения будет показано только определенное число пунктов. Автоматически будут добавлены навигационные пункты, таким образом, игроки легко могут возвращаться назад и вперед к определенным &amp;quot;страницам&amp;quot; пунктов меню.&lt;br /&gt;
**&amp;quot;Назад&amp;quot; всегда отображается как первый навигационный пункт, третий с конца поддерживаемых позиций. Не будет добавлен, если меню содержит только одну страницу. Если нет никаких предыдущих страниц, текст никогда не будет отображен, ни в каком стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 6&lt;br /&gt;
***Radio Стиль, позиция: 8&lt;br /&gt;
**&amp;quot;Вперед&amp;quot; всегда отображается как второй навигационный пункт, второй с конца из доступных позиций. Не отображается, если меню состоит из одной страницы. Если нет следующих страниц, то текс не будет отображен при любом стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 7&lt;br /&gt;
***Radio Стиль, позиция: 9&lt;br /&gt;
**&amp;quot;Выход&amp;quot; отображается, если у меню включена кнопка &amp;quot;выход&amp;quot;. Всегда занимает последнюю из возможных позиций&lt;br /&gt;
***Valve Стиль, позиция: 8&lt;br /&gt;
***Radio Стиль, позиция: 10&lt;br /&gt;
&lt;br /&gt;
Цель меню - упростить процедуру хранения, отображения, и вычисления выбранного пункта. Таким образом, меню не позволяет добавлять пустой текст, так как это значительно усложняет алгоритм отображения.  &lt;br /&gt;
&lt;br /&gt;
Внутренне, меню отображается через ''RenderMenu'' алогритм. Этот алгоритм создает временную панель и заполняет её пунктами из меню. Эта панель отображается клиенту. Алгоритм пытается создать свободное перемещение по всем меню, и по всем стилям. Таким образом, все меню отображаются через &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; класс, или &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; дескрипторы (Handles), и будет выглядеть и действовать так же, как и меню API, основанное на панельном API.&lt;br /&gt;
&lt;br /&gt;
=Обратные вызовы (callbacks)=&lt;br /&gt;
==Краткий обзор==&lt;br /&gt;
Меню базируются на системе обратных вызовов. Каждый обратный вызов предоставляет собой действие, которые происходят во время ''цикла отображения меню''. Цикл состоит из множества уведомлений:&lt;br /&gt;
*Начало уведомления.&lt;br /&gt;
**Уведомление об отображении, если меню может быть отображено клиенту.&lt;br /&gt;
**Уведомление о выборе пункта или кнопки отмены.&lt;br /&gt;
*Конец уведомления.&lt;br /&gt;
&lt;br /&gt;
Так как ''End'' обозначает конец полного цикла отображения, то обычно его используют для уничтожения временных меню.&lt;br /&gt;
&lt;br /&gt;
==Спецификация==&lt;br /&gt;
Детальное объяснение этих событий ниже. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель всегда доступен. Для SourcePawn, &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; хандл и &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; всегда установлены в &amp;lt;tt&amp;gt;MenuHandler&amp;lt;/tt&amp;gt; callback. В отличии от C++, SourcePawn API позволяет вызвать лишь некоторые действия, если они были запрошены во время создания меню. Это сделано в целях оптимизации. Однако, эти действия не могут быть не вызваны.&lt;br /&gt;
&lt;br /&gt;
*'''Start'''. Меню было признано. Это не означает, что меню будет отображено; однако, это гарантирует, что событие &amp;quot;OnMenuEnd&amp;quot; будет вызвано.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuStart()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
*'''Display'''.  Меню отобразилось клиенту.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuDisplay()&amp;lt;/tt&amp;gt; в C++. &amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; доступны указатель и индекс клиента.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Display&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Хандл меню.&lt;br /&gt;
*'''Select'''. Пункт был выбран. Будет дана позиция пункта в меню, а не нажатая кнопка (если меню не является панелью) &lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и позиция пункта.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Позиция пункта.&lt;br /&gt;
*'''Cancel'''.  Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
*'''End'''. Цикл отображения меню закончился; это означает, что &amp;quot;Start&amp;quot; действие было, и одно из действий &amp;quot;Select&amp;quot; или &amp;quot;Cancel&amp;quot; так же произошло. Обычно в этом месте отчищаются ресурсы меню или само меню удаляется.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuEnd()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Причина завершения меню.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: если param1 == MenuEnd_Cancelled, то здесь содержится код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Для панелей цикл жизни другой. Панели могут вызывать лишь два callback'а из написанных выше, и гарантируется, что один из них точно будет вызван для данного цикла отображения. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель будет всегда &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. Для SourcePawn, меню Handle будет всегда &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*'''Select'''.  Кнопка была нажата. Этой кнопкой может быть любое число. Например, если в панели у вас 2 пункта, то клиент может вызвать это событие, нажав &amp;quot;43&amp;quot;.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и нажатая кнопка.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Номер нажатой клавиши.&lt;br /&gt;
*'''Cancel'''. Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Примеры=&lt;br /&gt;
Для начала попробуем сделать очень простое меню:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы любите яблоки?&lt;br /&gt;
1. Да&lt;br /&gt;
2. Нет&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделаем это как меню и как панель, чтобы увидеть различия в их API.&lt;br /&gt;
&lt;br /&gt;
==Простое меню==&lt;br /&gt;
Сперва напишем наш пример через API Меню. Для пошагового руководства смотрите [[Menus Step By Step (SourceMod Scripting)]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду menu_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// хандл меню&lt;br /&gt;
int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
	/* Если был выбран какой-либо пункт, то сообщим клиенту о его выборе. */&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		char info[32]; // переменная для хранения выбора&lt;br /&gt;
		bool found = GetMenuItem(menu, param2, info, sizeof(info)); // получаем информацию о выбранном в меню пункте&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы нажали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info); // пишем клиенту в консоль&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню было отменено, то сообщим об этом серверу. */&lt;br /&gt;
	else if (action == MenuAction_Cancel)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл меню.  Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню &amp;quot;закончилось&amp;quot;, то удалим его из памяти */&lt;br /&gt;
	else if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		delete menu;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:menu = CreateMenu(MenuHandler1);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;да&amp;quot;, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;нет&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	DisplayMenu(menu, client, 20);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несколько очень важных замечаний о этом примере:&lt;br /&gt;
*Одно из &amp;lt;tt&amp;gt;Select&amp;lt;/tt&amp;gt; или &amp;lt;tt&amp;gt;Cancel&amp;lt;/tt&amp;gt; событий всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*&amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*Мы удаляем наше меню в &amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; действии, потому что наш дескриптор (Handle) нам больше не нужен. Если бы мы удалили его после &amp;lt;tt&amp;gt;DisplayMenu&amp;lt;/tt&amp;gt;, это бы отменило отображение меню клиенту.&lt;br /&gt;
*Меню по умолчанию имеет кнопку выхода. В нашем примере мы его отключили.&lt;br /&gt;
*Наше меню будет отображаться 20 секунд. Это означает, что если клиент ничего не выбрал в течении 20 секунд, то меню будет закрыто. Это обычно необходимо для меню с голосованиями. В отличии от AMX Mod X, вам не нужно создавать таймер, чтобы быть увереным, что меню &amp;quot;закончилось&amp;quot;.&lt;br /&gt;
*Мы создали и уничтожили новый дескриптор меню (Menu Handle), однако мы можем этого и не делать. Вполне допустимо создать дескриптор (Handle) один раз для всего времени жизни плагина.&lt;br /&gt;
*Чтобы нормально отображались русские буквы, кодировка файла должна быть UTF-8 (без BOOM), иначе будут квадратики вместо русских букв.&lt;br /&gt;
&lt;br /&gt;
Наше законченное меню и приложенный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика).&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовая панель==&lt;br /&gt;
Теперь давайте перепишем наш пример с использованием Панели вместо Меню.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду panel_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;panel_test1&amp;quot;, Panel_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public PanelHandler1(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d&amp;quot;, param2);&lt;br /&gt;
	} else if (action == MenuAction_Cancel) {&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл панель. Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Panel_Test1(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:panel = CreatePanel();&lt;br /&gt;
	SetPanelTitle(panel, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Да&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Нет&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
	SendPanelToClient(panel, client, PanelHandler1, 20);&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(panel);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как вы можете заметить, Панели сильно отличаются от Меню:&lt;br /&gt;
*Мы можем удалить Панель как только закончим его отображать. Мы можем создать Панель один раз, и продолжать использовать его много раз, однако мы можем удалить его в любой момент, без нарушения цикла отображения клиентского меню.&lt;br /&gt;
*Обработчик Панели (Handler) получает гораздо меньше информации. Так как Панели разрабатывались как грубый вывод текста, была сохранена возможность добавления информации без занятия слота. Поэтому обработчик может знать лишь об отмене или о нажатии любой клавиши с номером.&lt;br /&gt;
*Отсутствует автоматизация. Вы не можете добавить больше определенного числа выбираемых пунктов и добавить нумерацию. Автоматический контроль требует использования тяжелого объектного API Меню.&lt;br /&gt;
&lt;br /&gt;
Наше законченная панель и приложеный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_panel_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовое меню с нумерацией==&lt;br /&gt;
Теперь сделаем пример с большим функционалом -- нумерацией. Попробуем сделать меню для смены карты. Самый простой путь - прочесть &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл в начале плагина и создать меню по этому файла.&lt;br /&gt;
&lt;br /&gt;
Так как чтение и парсинг файла довольно долгая операция, мы будем делать это только раз за карту. Поэтому мы построим меню в &amp;lt;tt&amp;gt;OnMapStart&amp;lt;/tt&amp;gt;, и мы не будем вызывать &amp;lt;tt&amp;gt;CloseHandle&amp;lt;/tt&amp;gt; до события &amp;lt;tt&amp;gt;OnMapEnd&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:g_MapMenu = INVALID_HANDLE&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_changemap&amp;quot;, Command_ChangeMap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	g_MapMenu = BuildMapMenu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapEnd()&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu != INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		CloseHandle(g_MapMenu);&lt;br /&gt;
		g_MapMenu = INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Handle:BuildMapMenu()&lt;br /&gt;
{&lt;br /&gt;
	/* открываем файл */&lt;br /&gt;
	new Handle:file = OpenFile(&amp;quot;maplist.txt&amp;quot;, &amp;quot;rt&amp;quot;);&lt;br /&gt;
	if (file == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	/* создаем хандл меню */&lt;br /&gt;
	new Handle:menu = CreateMenu(Menu_ChangeMap);&lt;br /&gt;
	new String:mapname[255];&lt;br /&gt;
	while (!IsEndOfFile(file) &amp;amp;&amp;amp; ReadFileLine(file, mapname, sizeof(mapname)))&lt;br /&gt;
	{&lt;br /&gt;
		if (mapname[0] == ';' || !IsCharAlpha(mapname[0]))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* выбрасываем все пробелы из названия */&lt;br /&gt;
		new len = strlen(mapname);&lt;br /&gt;
		for (new i=0; i&amp;lt;len; i++)&lt;br /&gt;
		{&lt;br /&gt;
			if (IsCharSpace(mapname[i]))&lt;br /&gt;
			{&lt;br /&gt;
				mapname[i] = '\0';&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		/* проверяем карту на валидность */&lt;br /&gt;
		if (!IsMapValid(mapname))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* добавляем карту в меню */&lt;br /&gt;
		AddMenuItem(menu, mapname, mapname);&lt;br /&gt;
	}&lt;br /&gt;
	/* не забываем закрыть файл! */&lt;br /&gt;
	CloseHandle(file);&lt;br /&gt;
	&lt;br /&gt;
	/* наконец, устанавливаем заглавие меню */&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Выберите карту:&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return menu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Menu_ChangeMap(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		new String:info[32];&lt;br /&gt;
&lt;br /&gt;
		/* получаем информацию о пункте */&lt;br /&gt;
		new bool:found = GetMenuItem(menu, param2, info, sizeof(info));&lt;br /&gt;
&lt;br /&gt;
		/* говорим об этом клиенту */&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
&lt;br /&gt;
		/* меняем карту */&lt;br /&gt;
		ServerCommand(&amp;quot;changelevel %s&amp;quot;, info);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_ChangeMap(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Файл maplist.txt не был найден!&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}	&lt;br /&gt;
	&lt;br /&gt;
	DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результатом такого меню будет множество пунктов (&amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл содержал примерно 18 карт). Таким образом, конечное меню содержит три страницы, которые идут друг за другом, и выглядят вот так:&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_2_page1.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page2.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page3.PNG]]&lt;br /&gt;
&lt;br /&gt;
Сообщение в консоль будет выведено до смены карты, например, если выбрали &amp;lt;tt&amp;gt;cs_office&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы выбрали: 8 (найдено? 1 информация: cs_office)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отображение и разработка этого меню через &amp;lt;tt&amp;gt;ShowMenu&amp;lt;/tt&amp;gt; сообщение или &amp;lt;tt&amp;gt;Панельный&amp;lt;/tt&amp;gt; API занимает много времени и является довольно сложной задачей. Придется отслеживать все пункты в массиве большого размера, страницы, которые пользователь просматривает, и написать функцию, которая будет вычислять, на основе текущей странице, пункт, который был выбран и какую кнопку нажали. Меню системы это всё делает за вас.&lt;br /&gt;
&lt;br /&gt;
'''Заметки:'''&lt;br /&gt;
*Пункты управления, которые не доступны, не рисуются. Например, на первой странице вы не можете нажать &amp;quot;назад&amp;quot;, а на последней странице вы не можете нажать &amp;quot;вперед&amp;quot;. Несмотря на это, меню пытается удерживать каждый интерфейс последовательно. Таким образом, визуально, каждая навигационная клавиша всегда на одной и той же позиции.&lt;br /&gt;
*Если указать таймаут меню, то переключение между страницами не воздействует на общее время открытия меню. Например, если мы установим таймаут на 20 секунд, то каждое листание страницы будет устанавливать таймайт равный (20 - время, уже затраченное на просмотр). Только переоткрытие меню позволит вернуть таймаут времени на 20 секунд.&lt;br /&gt;
*Если отключить кнопку выхода, то не смотря на это, пункты 8 и 9 будут всё так же &amp;quot;назад&amp;quot; и вперед, соответственно.&lt;br /&gt;
*Мы не освобождаем дескриптор (Handle) меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, потому что наше меню глобальное/статическое, и нам не нужно пересоздавать его каждый раз.&lt;br /&gt;
*На изображении отображена кнопка &amp;quot;Назад&amp;quot;. В SourceMod версии 1011 и выше, кнопка &amp;quot;Назад&amp;quot; была переименована в &amp;quot;Предыдующая&amp;quot;, и &amp;quot;Назад&amp;quot; зарезервированно для &amp;quot;ExitBack&amp;quot; функциональности. (актуально для ENG версии, на ру вроде бы оба пункта &amp;quot;назад&amp;quot;. прим. переводчик)&lt;br /&gt;
&lt;br /&gt;
=Голосование=&lt;br /&gt;
SourceMod также имеет API для отображения меню как голосования для больше чем одного клиента. SourceMod автоматически обрабатываем выбранные пункты and randomly picking a tie-breaker (не знаю как перевести. прим. переводчика). Так же API голосования добавляет два новых значения для &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt;, которые во время отображения меню всегда вызываются.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteStart&amp;lt;/tt&amp;gt;: Вызывается после &amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt;, когда голосование оффициально началось.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;: Вызывается после того, как все клиенты проголосовали или отменили свои меню голосований. Выбранный предмет может быть получен через &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. Вызывается '''перед''' &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. Важно заметить, что это не замена &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, это разные вещи. Нельзя освобождать меню в &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;. '''Заметка:''' не вызывается, если используется &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteCancel&amp;lt;/tt&amp;gt;: Вызывается, если меню было отмеено в процессе голосования. Если вызвалось, то &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; или обратный вызов (callback) для результата голосований не будет вызваны, но &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; будет после этого.  Причина отмены голосования находится в &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Система меню - это то же самое меню, только с двумя дополнительными свойствами:&lt;br /&gt;
*Только одно голосование может быть активно. Вы должны проверять, не запущено ли голосование (&amp;lt;tt&amp;gt;IsVoteInProgress&amp;lt;/tt&amp;gt;()), иначе &amp;lt;tt&amp;gt;VoteMenu&amp;lt;/tt&amp;gt;() не сработает.&lt;br /&gt;
*Если клиент во время голосования отключится, то его голос будет недействительным. &lt;br /&gt;
&lt;br /&gt;
Пример внизу показывает, как создать функцию &amp;lt;tt&amp;gt;DoVoteMenu()&amp;lt;/tt&amp;gt;, которая будет спрашивать, хотят ли они сменить карту на предложенную.&lt;br /&gt;
&lt;br /&gt;
==Простое Голосование==&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Продвинутое Голосование==&lt;br /&gt;
Если вам нужно больше информации о результатах голосовании, чем вам дает &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;, вы можете указать отдельный обратный вызов (callback). Новый обратный вызов будет давать больше информации, но за опреденную цену: &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; не будет вызвано, и вам придется самим решать, как обработать результат. Это делается с помощью &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Handle_VoteResults(Handle:menu, &lt;br /&gt;
			num_votes, &lt;br /&gt;
			num_clients, &lt;br /&gt;
			const client_info[][2], &lt;br /&gt;
			num_items, &lt;br /&gt;
			const item_info[][2])&lt;br /&gt;
{&lt;br /&gt;
	/* Проверяем, нет ли сразу двух победителей */&lt;br /&gt;
	new winner = 0;&lt;br /&gt;
	if (num_items &amp;gt; 1&lt;br /&gt;
	    &amp;amp;&amp;amp; (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))&lt;br /&gt;
	{&lt;br /&gt;
		winner = GetRandomInt(0, 1);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	new String:map[64];&lt;br /&gt;
	GetMenuItem(menu, item_info[winner][VOTEINFO_ITEM_INDEX], map, sizeof(map));&lt;br /&gt;
	ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetVoteResultCallback(menu, Handle_VoteResults);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=ExitBack=&lt;br /&gt;
ExitBack - специальный термин для обозначения &amp;quot;ExitBack Кнопки&amp;quot;. По умолчанию эта кнопка отключена.  Обычно нумерованные меню не имеют пункта &amp;quot;Назад&amp;quot; для первой страници. Если &amp;quot;ExitBack&amp;quot; кнопка включена, появится кнопка &amp;quot;Назад&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
Выбор опции &amp;quot;ExitBack&amp;quot; для меню в обратном вызове будет в действии &amp;lt;tt&amp;gt;MenuCancel_ExitBack&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;MenuEnd_ExitBack&amp;lt;/tt&amp;gt;. Функциональность этого такая же, как и у простого выхода из меню; дополнительная функциональность должна быть определена через обратный вызов.&lt;br /&gt;
&lt;br /&gt;
=Освобождение Дескрипторов (Handle) меню=&lt;br /&gt;
Важно закрывать дескриптор меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; вызывается когда меню закрылось и больше не нужно.&lt;br /&gt;
&lt;br /&gt;
=Переводы=&lt;br /&gt;
Есть возможность динамически переводить меню для каждого игрока через &amp;lt;tt&amp;gt;MenuAction_DisplayItem&amp;lt;/tt&amp;gt; callback. Специальный натив, &amp;lt;tt&amp;gt;RedrawMenuItem&amp;lt;/tt&amp;gt;, используется чтобы преобразовать текст внутри вызова. Давайте переделаем наш пример голосования с переводами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	} else if (action == MenuAction_DisplayItem) {&lt;br /&gt;
		/* Получаем отображаемую строку, будем использовать как фразу перевода */&lt;br /&gt;
		decl String:display[64];&lt;br /&gt;
		GetMenuItem(menu, param2, &amp;quot;&amp;quot;, 0, _, display, sizeof(display));&lt;br /&gt;
&lt;br /&gt;
		/* Переводим строку на язык клиента */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, display, param1);&lt;br /&gt;
&lt;br /&gt;
		/* Перерисовываем текст */&lt;br /&gt;
		return RedrawMenuItem(buffer);&lt;br /&gt;
	} else if (action == MenuAction_Display) {&lt;br /&gt;
		/* Дескриптор панели будет вторым параметром */&lt;br /&gt;
		new Handle:panel = Handle:param2;&lt;br /&gt;
		&lt;br /&gt;
		/* Получаем название карты, на которую меняем, из первого пункта */&lt;br /&gt;
		decl String:map[64];&lt;br /&gt;
		GetMenuItem(menu, 0, map, sizeof(map));&lt;br /&gt;
		&lt;br /&gt;
		/* Переводим нашу фразу */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Change map to?&amp;quot;, client, map);&lt;br /&gt;
&lt;br /&gt;
		SetPanelTitle(panel, buffer);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu, MenuAction_DisplayItem|MenuAction_Display);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Ru:Menu_API_(SourceMod)&amp;diff=11212</id>
		<title>Ru:Menu API (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Ru:Menu_API_(SourceMod)&amp;diff=11212"/>
		<updated>2021-07-20T13:41:00Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Kleiner moved page Ru:Menu API (SourceMod) to Menu API (SourceMod)=ru: testing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Menu API (SourceMod)=ru]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11210</id>
		<title>Menu API (SourceMod)=ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11210"/>
		<updated>2021-07-20T13:40:21Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Menu API (SourceMod)}}&lt;br /&gt;
SourceMod обладает большим API для создания и отображений меню для клиентов. В отличии от AMX Mod X, API SourceMod'a обладает широким функционалом. Меню базируются на обратных вызовах (callbacks), которые гарантированно будут вызваны.&lt;br /&gt;
&lt;br /&gt;
Для C++, API меню может быть найдено в &amp;lt;tt&amp;gt;public/IMenuManager.h&amp;lt;/tt&amp;gt;. Для SourcePawn - &amp;lt;tt&amp;gt;scripting/include/menus.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Объекты=&lt;br /&gt;
Система меню SourceMod основана на объектно ориентированной иерархии. Понимание этой иерархии, даже для скриптинга, является ключевым моментом для эффективного использования меню.&lt;br /&gt;
&lt;br /&gt;
==Стили==&lt;br /&gt;
Высший уровень объектов называется ''MenuStyle'' (&amp;lt;tt&amp;gt;IMenuStyle&amp;lt;/tt&amp;gt; в C++). Стили описывают уникальную систему меню. Всего есть два стиля, встроенных в SourceMod:&lt;br /&gt;
*Valve Styly, так же называемым &amp;quot;ESC&amp;quot; меню; 8 пунктов на странице, нельзя добавить текст без занятия слота или спрятать (отключить) текст.&lt;br /&gt;
*Radio Style, так же называемый &amp;quot;AMX&amp;quot; меню; 10 пунктов на странице, можно добавить любой текст.&lt;br /&gt;
&lt;br /&gt;
Каждый MenuStyle обладает своими правилами и свойствами. Вы можете считать, что каждый из них существует на раздельных &amp;quot;каналах&amp;quot;. Например, два разных меню могут быть на экране игрока, будучи Valve и Radio меню одновременно, и SourceMod сможет обработать их обоих без всяких проблем. Это всё возможно благодаря тому, что каждый стиль отслеживает свои меню отдельно.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Меню низшего уровня называется ''Панелью'' (&amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; в C++). Панель - один ''''кусок'''' текста. Пункты и текст могут добавляться к панели столько раз, сколько поддерживает родительский стиль. Например, Valve стиль не поддерживает отображение текста без занятия слота или отключенных пунктов. Однако с Radio стилем панели это становится возможным, и вы можете отобразить большое количество текста так, как вам это захочется. &lt;br /&gt;
&lt;br /&gt;
Панель считается временным объектом. Она создается, рендерится, отображается, и затем удаляется. Хотя она может быть сохранена на неопределенное время, если в этом есть необходимость.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Vavle стиля:&lt;br /&gt;
*Максимум пунктов на странице: 8&lt;br /&gt;
*Отключенный текст не отображается.&lt;br /&gt;
*Нельзя написать текст, не заняв слота.&lt;br /&gt;
*Пробелы не добавляются к пробелам\новым линиям, добавляя чувство &amp;quot;ограниченности&amp;quot;. &lt;br /&gt;
*Пользователь должен нажать &amp;quot;ESC&amp;quot; или быть в своей консоли, чтобы увидеть меню.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Radio стиля:&lt;br /&gt;
*10 пунктов на странице&lt;br /&gt;
*Заглавия белые; пункты желтые, если не отключены. Если отключены, то белого цвета.&lt;br /&gt;
*Нулевой элемент всегда белый. Для согласованности это значит, что навигационное управление всегда белое и находится в следующей секции, и просто не отрисовывается, если отсутствует.&lt;br /&gt;
&lt;br /&gt;
==Меню==&lt;br /&gt;
Наконец, есть просто ''Меню'' (&amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; in C++). Это вспомогательный объект, созданный для хранения базированных на меню выбираемых пунктов. В отличии от низкоуровневых панелей, меню содержит '''пункты''', и может содержать только те пункты, которые можно выбрать (т.е. занимающие слоты). Они делятся на две категории:&lt;br /&gt;
*Не нумерованные: эти меню могут иметь определенное число пунктов, и не имеют навигационных опций, кроме кнопки &amp;quot;Выход&amp;quot;, которая всегда добавляется на последнюю позицию, поддерживаемую стилем.&lt;br /&gt;
**Valve Стиль: максимум пунктов: 8&lt;br /&gt;
**Radio Стиль: максимум пунктов: 10&lt;br /&gt;
*Нумерованные: такие меню могут содержать сколь угодно пунктов. Во время отображения будет показано только определенное число пунктов. Автоматически будут добавлены навигационные пункты, таким образом, игроки легко могут возвращаться назад и вперед к определенным &amp;quot;страницам&amp;quot; пунктов меню.&lt;br /&gt;
**&amp;quot;Назад&amp;quot; всегда отображается как первый навигационный пункт, третий с конца поддерживаемых позиций. Не будет добавлен, если меню содержит только одну страницу. Если нет никаких предыдущих страниц, текст никогда не будет отображен, ни в каком стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 6&lt;br /&gt;
***Radio Стиль, позиция: 8&lt;br /&gt;
**&amp;quot;Вперед&amp;quot; всегда отображается как второй навигационный пункт, второй с конца из доступных позиций. Не отображается, если меню состоит из одной страницы. Если нет следующих страниц, то текс не будет отображен при любом стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 7&lt;br /&gt;
***Radio Стиль, позиция: 9&lt;br /&gt;
**&amp;quot;Выход&amp;quot; отображается, если у меню включена кнопка &amp;quot;выход&amp;quot;. Всегда занимает последнюю из возможных позиций&lt;br /&gt;
***Valve Стиль, позиция: 8&lt;br /&gt;
***Radio Стиль, позиция: 10&lt;br /&gt;
&lt;br /&gt;
Цель меню - упростить процедуру хранения, отображения, и вычисления выбранного пункта. Таким образом, меню не позволяет добавлять пустой текст, так как это значительно усложняет алгоритм отображения.  &lt;br /&gt;
&lt;br /&gt;
Внутренне, меню отображается через ''RenderMenu'' алогритм. Этот алгоритм создает временную панель и заполняет её пунктами из меню. Эта панель отображается клиенту. Алгоритм пытается создать свободное перемещение по всем меню, и по всем стилям. Таким образом, все меню отображаются через &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; класс, или &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; дескрипторы (Handles), и будет выглядеть и действовать так же, как и меню API, основанное на панельном API.&lt;br /&gt;
&lt;br /&gt;
=Обратные вызовы (callbacks)=&lt;br /&gt;
==Краткий обзор==&lt;br /&gt;
Меню базируются на системе обратных вызовов. Каждый обратный вызов предоставляет собой действие, которые происходят во время ''цикла отображения меню''. Цикл состоит из множества уведомлений:&lt;br /&gt;
*Начало уведомления.&lt;br /&gt;
**Уведомление об отображении, если меню может быть отображено клиенту.&lt;br /&gt;
**Уведомление о выборе пункта или кнопки отмены.&lt;br /&gt;
*Конец уведомления.&lt;br /&gt;
&lt;br /&gt;
Так как ''End'' обозначает конец полного цикла отображения, то обычно его используют для уничтожения временных меню.&lt;br /&gt;
&lt;br /&gt;
==Спецификация==&lt;br /&gt;
Детальное объяснение этих событий ниже. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель всегда доступен. Для SourcePawn, &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; хандл и &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; всегда установлены в &amp;lt;tt&amp;gt;MenuHandler&amp;lt;/tt&amp;gt; callback. В отличии от C++, SourcePawn API позволяет вызвать лишь некоторые действия, если они были запрошены во время создания меню. Это сделано в целях оптимизации. Однако, эти действия не могут быть не вызваны.&lt;br /&gt;
&lt;br /&gt;
*'''Start'''. Меню было признано. Это не означает, что меню будет отображено; однако, это гарантирует, что событие &amp;quot;OnMenuEnd&amp;quot; будет вызвано.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuStart()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
*'''Display'''.  Меню отобразилось клиенту.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuDisplay()&amp;lt;/tt&amp;gt; в C++. &amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; доступны указатель и индекс клиента.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Display&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Хандл меню.&lt;br /&gt;
*'''Select'''. Пункт был выбран. Будет дана позиция пункта в меню, а не нажатая кнопка (если меню не является панелью) &lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и позиция пункта.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Позиция пункта.&lt;br /&gt;
*'''Cancel'''.  Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
*'''End'''. Цикл отображения меню закончился; это означает, что &amp;quot;Start&amp;quot; действие было, и одно из действий &amp;quot;Select&amp;quot; или &amp;quot;Cancel&amp;quot; так же произошло. Обычно в этом месте отчищаются ресурсы меню или само меню удаляется.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuEnd()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Причина завершения меню.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: если param1 == MenuEnd_Cancelled, то здесь содержится код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Для панелей цикл жизни другой. Панели могут вызывать лишь два callback'а из написанных выше, и гарантируется, что один из них точно будет вызван для данного цикла отображения. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель будет всегда &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. Для SourcePawn, меню Handle будет всегда &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*'''Select'''.  Кнопка была нажата. Этой кнопкой может быть любое число. Например, если в панели у вас 2 пункта, то клиент может вызвать это событие, нажав &amp;quot;43&amp;quot;.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и нажатая кнопка.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Номер нажатой клавиши.&lt;br /&gt;
*'''Cancel'''. Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Примеры=&lt;br /&gt;
Для начала попробуем сделать очень простое меню:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы любите яблоки?&lt;br /&gt;
1. Да&lt;br /&gt;
2. Нет&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделаем это как меню и как панель, чтобы увидеть различия в их API.&lt;br /&gt;
&lt;br /&gt;
==Простое меню==&lt;br /&gt;
Сперва напишем наш пример через API Меню. Для пошагового руководства смотрите [[Menus Step By Step (SourceMod Scripting)]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду menu_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// хандл меню&lt;br /&gt;
int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
	/* Если был выбран какой-либо пункт, то сообщим клиенту о его выборе. */&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		char info[32]; // переменная для хранения выбора&lt;br /&gt;
		bool found = GetMenuItem(menu, param2, info, sizeof(info)); // получаем информацию о выбранном в меню пункте&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы нажали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info); // пишем клиенту в консоль&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню было отменено, то сообщим об этом серверу. */&lt;br /&gt;
	else if (action == MenuAction_Cancel)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл меню.  Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню &amp;quot;закончилось&amp;quot;, то удалим его из памяти */&lt;br /&gt;
	else if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		delete menu;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:menu = CreateMenu(MenuHandler1);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;да&amp;quot;, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;нет&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	DisplayMenu(menu, client, 20);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несколько очень важных замечаний о этом примере:&lt;br /&gt;
*Одно из &amp;lt;tt&amp;gt;Select&amp;lt;/tt&amp;gt; или &amp;lt;tt&amp;gt;Cancel&amp;lt;/tt&amp;gt; событий всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*&amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*Мы удаляем наше меню в &amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; действии, потому что наш дескриптор (Handle) нам больше не нужен. Если бы мы удалили его после &amp;lt;tt&amp;gt;DisplayMenu&amp;lt;/tt&amp;gt;, это бы отменило отображение меню клиенту.&lt;br /&gt;
*Меню по умолчанию имеет кнопку выхода. В нашем примере мы его отключили.&lt;br /&gt;
*Наше меню будет отображаться 20 секунд. Это означает, что если клиент ничего не выбрал в течении 20 секунд, то меню будет закрыто. Это обычно необходимо для меню с голосованиями. В отличии от AMX Mod X, вам не нужно создавать таймер, чтобы быть увереным, что меню &amp;quot;закончилось&amp;quot;.&lt;br /&gt;
*Мы создали и уничтожили новый дескриптор меню (Menu Handle), однако мы можем этого и не делать. Вполне допустимо создать дескриптор (Handle) один раз для всего времени жизни плагина.&lt;br /&gt;
*Чтобы нормально отображались русские буквы, кодировка файла должна быть UTF-8 (без BOOM), иначе будут квадратики вместо русских букв.&lt;br /&gt;
&lt;br /&gt;
Наше законченное меню и приложенный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика).&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовая панель==&lt;br /&gt;
Теперь давайте перепишем наш пример с использованием Панели вместо Меню.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду panel_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;panel_test1&amp;quot;, Panel_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public PanelHandler1(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d&amp;quot;, param2);&lt;br /&gt;
	} else if (action == MenuAction_Cancel) {&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл панель. Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Panel_Test1(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:panel = CreatePanel();&lt;br /&gt;
	SetPanelTitle(panel, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Да&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Нет&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
	SendPanelToClient(panel, client, PanelHandler1, 20);&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(panel);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как вы можете заметить, Панели сильно отличаются от Меню:&lt;br /&gt;
*Мы можем удалить Панель как только закончим его отображать. Мы можем создать Панель один раз, и продолжать использовать его много раз, однако мы можем удалить его в любой момент, без нарушения цикла отображения клиентского меню.&lt;br /&gt;
*Обработчик Панели (Handler) получает гораздо меньше информации. Так как Панели разрабатывались как грубый вывод текста, была сохранена возможность добавления информации без занятия слота. Поэтому обработчик может знать лишь об отмене или о нажатии любой клавиши с номером.&lt;br /&gt;
*Отсутствует автоматизация. Вы не можете добавить больше определенного числа выбираемых пунктов и добавить нумерацию. Автоматический контроль требует использования тяжелого объектного API Меню.&lt;br /&gt;
&lt;br /&gt;
Наше законченная панель и приложеный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_panel_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовое меню с нумерацией==&lt;br /&gt;
Теперь сделаем пример с большим функционалом -- нумерацией. Попробуем сделать меню для смены карты. Самый простой путь - прочесть &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл в начале плагина и создать меню по этому файла.&lt;br /&gt;
&lt;br /&gt;
Так как чтение и парсинг файла довольно долгая операция, мы будем делать это только раз за карту. Поэтому мы построим меню в &amp;lt;tt&amp;gt;OnMapStart&amp;lt;/tt&amp;gt;, и мы не будем вызывать &amp;lt;tt&amp;gt;CloseHandle&amp;lt;/tt&amp;gt; до события &amp;lt;tt&amp;gt;OnMapEnd&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:g_MapMenu = INVALID_HANDLE&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_changemap&amp;quot;, Command_ChangeMap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	g_MapMenu = BuildMapMenu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapEnd()&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu != INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		CloseHandle(g_MapMenu);&lt;br /&gt;
		g_MapMenu = INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Handle:BuildMapMenu()&lt;br /&gt;
{&lt;br /&gt;
	/* открываем файл */&lt;br /&gt;
	new Handle:file = OpenFile(&amp;quot;maplist.txt&amp;quot;, &amp;quot;rt&amp;quot;);&lt;br /&gt;
	if (file == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	/* создаем хандл меню */&lt;br /&gt;
	new Handle:menu = CreateMenu(Menu_ChangeMap);&lt;br /&gt;
	new String:mapname[255];&lt;br /&gt;
	while (!IsEndOfFile(file) &amp;amp;&amp;amp; ReadFileLine(file, mapname, sizeof(mapname)))&lt;br /&gt;
	{&lt;br /&gt;
		if (mapname[0] == ';' || !IsCharAlpha(mapname[0]))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* выбрасываем все пробелы из названия */&lt;br /&gt;
		new len = strlen(mapname);&lt;br /&gt;
		for (new i=0; i&amp;lt;len; i++)&lt;br /&gt;
		{&lt;br /&gt;
			if (IsCharSpace(mapname[i]))&lt;br /&gt;
			{&lt;br /&gt;
				mapname[i] = '\0';&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		/* проверяем карту на валидность */&lt;br /&gt;
		if (!IsMapValid(mapname))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* добавляем карту в меню */&lt;br /&gt;
		AddMenuItem(menu, mapname, mapname);&lt;br /&gt;
	}&lt;br /&gt;
	/* не забываем закрыть файл! */&lt;br /&gt;
	CloseHandle(file);&lt;br /&gt;
	&lt;br /&gt;
	/* наконец, устанавливаем заглавие меню */&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Выберите карту:&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return menu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Menu_ChangeMap(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		new String:info[32];&lt;br /&gt;
&lt;br /&gt;
		/* получаем информацию о пункте */&lt;br /&gt;
		new bool:found = GetMenuItem(menu, param2, info, sizeof(info));&lt;br /&gt;
&lt;br /&gt;
		/* говорим об этом клиенту */&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
&lt;br /&gt;
		/* меняем карту */&lt;br /&gt;
		ServerCommand(&amp;quot;changelevel %s&amp;quot;, info);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_ChangeMap(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Файл maplist.txt не был найден!&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}	&lt;br /&gt;
	&lt;br /&gt;
	DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результатом такого меню будет множество пунктов (&amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл содержал примерно 18 карт). Таким образом, конечное меню содержит три страницы, которые идут друг за другом, и выглядят вот так:&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_2_page1.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page2.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page3.PNG]]&lt;br /&gt;
&lt;br /&gt;
Сообщение в консоль будет выведено до смены карты, например, если выбрали &amp;lt;tt&amp;gt;cs_office&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы выбрали: 8 (найдено? 1 информация: cs_office)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отображение и разработка этого меню через &amp;lt;tt&amp;gt;ShowMenu&amp;lt;/tt&amp;gt; сообщение или &amp;lt;tt&amp;gt;Панельный&amp;lt;/tt&amp;gt; API занимает много времени и является довольно сложной задачей. Придется отслеживать все пункты в массиве большого размера, страницы, которые пользователь просматривает, и написать функцию, которая будет вычислять, на основе текущей странице, пункт, который был выбран и какую кнопку нажали. Меню системы это всё делает за вас.&lt;br /&gt;
&lt;br /&gt;
'''Заметки:'''&lt;br /&gt;
*Пункты управления, которые не доступны, не рисуются. Например, на первой странице вы не можете нажать &amp;quot;назад&amp;quot;, а на последней странице вы не можете нажать &amp;quot;вперед&amp;quot;. Несмотря на это, меню пытается удерживать каждый интерфейс последовательно. Таким образом, визуально, каждая навигационная клавиша всегда на одной и той же позиции.&lt;br /&gt;
*Если указать таймаут меню, то переключение между страницами не воздействует на общее время открытия меню. Например, если мы установим таймаут на 20 секунд, то каждое листание страницы будет устанавливать таймайт равный (20 - время, уже затраченное на просмотр). Только переоткрытие меню позволит вернуть таймаут времени на 20 секунд.&lt;br /&gt;
*Если отключить кнопку выхода, то не смотря на это, пункты 8 и 9 будут всё так же &amp;quot;назад&amp;quot; и вперед, соответственно.&lt;br /&gt;
*Мы не освобождаем дескриптор (Handle) меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, потому что наше меню глобальное/статическое, и нам не нужно пересоздавать его каждый раз.&lt;br /&gt;
*На изображении отображена кнопка &amp;quot;Назад&amp;quot;. В SourceMod версии 1011 и выше, кнопка &amp;quot;Назад&amp;quot; была переименована в &amp;quot;Предыдующая&amp;quot;, и &amp;quot;Назад&amp;quot; зарезервированно для &amp;quot;ExitBack&amp;quot; функциональности. (актуально для ENG версии, на ру вроде бы оба пункта &amp;quot;назад&amp;quot;. прим. переводчик)&lt;br /&gt;
&lt;br /&gt;
=Голосование=&lt;br /&gt;
SourceMod также имеет API для отображения меню как голосования для больше чем одного клиента. SourceMod автоматически обрабатываем выбранные пункты and randomly picking a tie-breaker (не знаю как перевести. прим. переводчика). Так же API голосования добавляет два новых значения для &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt;, которые во время отображения меню всегда вызываются.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteStart&amp;lt;/tt&amp;gt;: Вызывается после &amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt;, когда голосование оффициально началось.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;: Вызывается после того, как все клиенты проголосовали или отменили свои меню голосований. Выбранный предмет может быть получен через &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. Вызывается '''перед''' &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. Важно заметить, что это не замена &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, это разные вещи. Нельзя освобождать меню в &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;. '''Заметка:''' не вызывается, если используется &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteCancel&amp;lt;/tt&amp;gt;: Вызывается, если меню было отмеено в процессе голосования. Если вызвалось, то &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; или обратный вызов (callback) для результата голосований не будет вызваны, но &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; будет после этого.  Причина отмены голосования находится в &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Система меню - это то же самое меню, только с двумя дополнительными свойствами:&lt;br /&gt;
*Только одно голосование может быть активно. Вы должны проверять, не запущено ли голосование (&amp;lt;tt&amp;gt;IsVoteInProgress&amp;lt;/tt&amp;gt;()), иначе &amp;lt;tt&amp;gt;VoteMenu&amp;lt;/tt&amp;gt;() не сработает.&lt;br /&gt;
*Если клиент во время голосования отключится, то его голос будет недействительным. &lt;br /&gt;
&lt;br /&gt;
Пример внизу показывает, как создать функцию &amp;lt;tt&amp;gt;DoVoteMenu()&amp;lt;/tt&amp;gt;, которая будет спрашивать, хотят ли они сменить карту на предложенную.&lt;br /&gt;
&lt;br /&gt;
==Простое Голосование==&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Продвинутое Голосование==&lt;br /&gt;
Если вам нужно больше информации о результатах голосовании, чем вам дает &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;, вы можете указать отдельный обратный вызов (callback). Новый обратный вызов будет давать больше информации, но за опреденную цену: &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; не будет вызвано, и вам придется самим решать, как обработать результат. Это делается с помощью &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Handle_VoteResults(Handle:menu, &lt;br /&gt;
			num_votes, &lt;br /&gt;
			num_clients, &lt;br /&gt;
			const client_info[][2], &lt;br /&gt;
			num_items, &lt;br /&gt;
			const item_info[][2])&lt;br /&gt;
{&lt;br /&gt;
	/* Проверяем, нет ли сразу двух победителей */&lt;br /&gt;
	new winner = 0;&lt;br /&gt;
	if (num_items &amp;gt; 1&lt;br /&gt;
	    &amp;amp;&amp;amp; (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))&lt;br /&gt;
	{&lt;br /&gt;
		winner = GetRandomInt(0, 1);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	new String:map[64];&lt;br /&gt;
	GetMenuItem(menu, item_info[winner][VOTEINFO_ITEM_INDEX], map, sizeof(map));&lt;br /&gt;
	ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetVoteResultCallback(menu, Handle_VoteResults);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=ExitBack=&lt;br /&gt;
ExitBack - специальный термин для обозначения &amp;quot;ExitBack Кнопки&amp;quot;. По умолчанию эта кнопка отключена.  Обычно нумерованные меню не имеют пункта &amp;quot;Назад&amp;quot; для первой страници. Если &amp;quot;ExitBack&amp;quot; кнопка включена, появится кнопка &amp;quot;Назад&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
Выбор опции &amp;quot;ExitBack&amp;quot; для меню в обратном вызове будет в действии &amp;lt;tt&amp;gt;MenuCancel_ExitBack&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;MenuEnd_ExitBack&amp;lt;/tt&amp;gt;. Функциональность этого такая же, как и у простого выхода из меню; дополнительная функциональность должна быть определена через обратный вызов.&lt;br /&gt;
&lt;br /&gt;
=Освобождение Дескрипторов (Handle) меню=&lt;br /&gt;
Важно закрывать дескриптор меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; вызывается когда меню закрылось и больше не нужно.&lt;br /&gt;
&lt;br /&gt;
=Переводы=&lt;br /&gt;
Есть возможность динамически переводить меню для каждого игрока через &amp;lt;tt&amp;gt;MenuAction_DisplayItem&amp;lt;/tt&amp;gt; callback. Специальный натив, &amp;lt;tt&amp;gt;RedrawMenuItem&amp;lt;/tt&amp;gt;, используется чтобы преобразовать текст внутри вызова. Давайте переделаем наш пример голосования с переводами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	} else if (action == MenuAction_DisplayItem) {&lt;br /&gt;
		/* Получаем отображаемую строку, будем использовать как фразу перевода */&lt;br /&gt;
		decl String:display[64];&lt;br /&gt;
		GetMenuItem(menu, param2, &amp;quot;&amp;quot;, 0, _, display, sizeof(display));&lt;br /&gt;
&lt;br /&gt;
		/* Переводим строку на язык клиента */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, display, param1);&lt;br /&gt;
&lt;br /&gt;
		/* Перерисовываем текст */&lt;br /&gt;
		return RedrawMenuItem(buffer);&lt;br /&gt;
	} else if (action == MenuAction_Display) {&lt;br /&gt;
		/* Дескриптор панели будет вторым параметром */&lt;br /&gt;
		new Handle:panel = Handle:param2;&lt;br /&gt;
		&lt;br /&gt;
		/* Получаем название карты, на которую меняем, из первого пункта */&lt;br /&gt;
		decl String:map[64];&lt;br /&gt;
		GetMenuItem(menu, 0, map, sizeof(map));&lt;br /&gt;
		&lt;br /&gt;
		/* Переводим нашу фразу */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Change map to?&amp;quot;, client, map);&lt;br /&gt;
&lt;br /&gt;
		SetPanelTitle(panel, buffer);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu, MenuAction_DisplayItem|MenuAction_Display);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11208</id>
		<title>Menu API (SourceMod)=ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11208"/>
		<updated>2021-07-20T13:38:17Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Kleiner moved page Menu API (SourceMod)/ru to Ru:Menu API (SourceMod) over redirect: sorry&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Menu API (SourceMod)}}&lt;br /&gt;
SourceMod обладает большим API для создания и отображений меню для клиентов. В отличии от AMX Mod X, API SourceMod'a обладает широким функционалом. Меню базируются на обратных вызовах (callbacks), которые гарантированно будут вызваны.&lt;br /&gt;
&lt;br /&gt;
Для C++, API меню может быть найдено в &amp;lt;tt&amp;gt;public/IMenuManager.h&amp;lt;/tt&amp;gt;. Для SourcePawn - &amp;lt;tt&amp;gt;scripting/include/menus.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Объекты=&lt;br /&gt;
Система меню SourceMod основана на объектно ориентированной иерархии. Понимание этой иерархии, даже для скриптинга, является ключевым моментом для эффективного использования меню.&lt;br /&gt;
&lt;br /&gt;
==Стили==&lt;br /&gt;
Высший уровень объектов называется ''MenuStyle'' (&amp;lt;tt&amp;gt;IMenuStyle&amp;lt;/tt&amp;gt; в C++). Стили описывают уникальную систему меню. Всего есть два стиля, встроенных в SourceMod:&lt;br /&gt;
*Valve Styly, так же называемым &amp;quot;ESC&amp;quot; меню; 8 пунктов на странице, нельзя добавить текст без занятия слота или спрятать (отключить) текст.&lt;br /&gt;
*Radio Style, так же называемый &amp;quot;AMX&amp;quot; меню; 10 пунктов на странице, можно добавить любой текст.&lt;br /&gt;
&lt;br /&gt;
Каждый MenuStyle обладает своими правилами и свойствами. Вы можете считать, что каждый из них существует на раздельных &amp;quot;каналах&amp;quot;. Например, два разных меню могут быть на экране игрока, будучи Valve и Radio меню одновременно, и SourceMod сможет обработать их обоих без всяких проблем. Это всё возможно благодаря тому, что каждый стиль отслеживает свои меню отдельно.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Меню низшего уровня называется ''Панелью'' (&amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; в C++). Панель - один ''''кусок'''' текста. Пункты и текст могут добавляться к панели столько раз, сколько поддерживает родительский стиль. Например, Valve стиль не поддерживает отображение текста без занятия слота или отключенных пунктов. Однако с Radio стилем панели это становится возможным, и вы можете отобразить большое количество текста так, как вам это захочется. &lt;br /&gt;
&lt;br /&gt;
Панель считается временным объектом. Она создается, рендерится, отображается, и затем удаляется. Хотя она может быть сохранена на неопределенное время, если в этом есть необходимость.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Vavle стиля:&lt;br /&gt;
*Максимум пунктов на странице: 8&lt;br /&gt;
*Отключенный текст не отображается.&lt;br /&gt;
*Нельзя написать текст, не заняв слота.&lt;br /&gt;
*Пробелы не добавляются к пробелам\новым линиям, добавляя чувство &amp;quot;ограниченности&amp;quot;. &lt;br /&gt;
*Пользователь должен нажать &amp;quot;ESC&amp;quot; или быть в своей консоли, чтобы увидеть меню.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Radio стиля:&lt;br /&gt;
*10 пунктов на странице&lt;br /&gt;
*Заглавия белые; пункты желтые, если не отключены. Если отключены, то белого цвета.&lt;br /&gt;
*Нулевой элемент всегда белый. Для согласованности это значит, что навигационное управление всегда белое и находится в следующей секции, и просто не отрисовывается, если отсутствует.&lt;br /&gt;
&lt;br /&gt;
==Меню==&lt;br /&gt;
Наконец, есть просто ''Меню'' (&amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; in C++). Это вспомогательный объект, созданный для хранения базированных на меню выбираемых пунктов. В отличии от низкоуровневых панелей, меню содержит '''пункты''', и может содержать только те пункты, которые можно выбрать (т.е. занимающие слоты). Они делятся на две категории:&lt;br /&gt;
*Не нумерованные: эти меню могут иметь определенное число пунктов, и не имеют навигационных опций, кроме кнопки &amp;quot;Выход&amp;quot;, которая всегда добавляется на последнюю позицию, поддерживаемую стилем.&lt;br /&gt;
**Valve Стиль: максимум пунктов: 8&lt;br /&gt;
**Radio Стиль: максимум пунктов: 10&lt;br /&gt;
*Нумерованные: такие меню могут содержать сколь угодно пунктов. Во время отображения будет показано только определенное число пунктов. Автоматически будут добавлены навигационные пункты, таким образом, игроки легко могут возвращаться назад и вперед к определенным &amp;quot;страницам&amp;quot; пунктов меню.&lt;br /&gt;
**&amp;quot;Назад&amp;quot; всегда отображается как первый навигационный пункт, третий с конца поддерживаемых позиций. Не будет добавлен, если меню содержит только одну страницу. Если нет никаких предыдущих страниц, текст никогда не будет отображен, ни в каком стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 6&lt;br /&gt;
***Radio Стиль, позиция: 8&lt;br /&gt;
**&amp;quot;Вперед&amp;quot; всегда отображается как второй навигационный пункт, второй с конца из доступных позиций. Не отображается, если меню состоит из одной страницы. Если нет следующих страниц, то текс не будет отображен при любом стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 7&lt;br /&gt;
***Radio Стиль, позиция: 9&lt;br /&gt;
**&amp;quot;Выход&amp;quot; отображается, если у меню включена кнопка &amp;quot;выход&amp;quot;. Всегда занимает последнюю из возможных позиций&lt;br /&gt;
***Valve Стиль, позиция: 8&lt;br /&gt;
***Radio Стиль, позиция: 10&lt;br /&gt;
&lt;br /&gt;
Цель меню - упростить процедуру хранения, отображения, и вычисления выбранного пункта. Таким образом, меню не позволяет добавлять пустой текст, так как это значительно усложняет алгоритм отображения.  &lt;br /&gt;
&lt;br /&gt;
Внутренне, меню отображается через ''RenderMenu'' алогритм. Этот алгоритм создает временную панель и заполняет её пунктами из меню. Эта панель отображается клиенту. Алгоритм пытается создать свободное перемещение по всем меню, и по всем стилям. Таким образом, все меню отображаются через &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; класс, или &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; дескрипторы (Handles), и будет выглядеть и действовать так же, как и меню API, основанное на панельном API.&lt;br /&gt;
&lt;br /&gt;
=Обратные вызовы (callbacks)=&lt;br /&gt;
==Краткий обзор==&lt;br /&gt;
Меню базируются на системе обратных вызовов. Каждый обратный вызов предоставляет собой действие, которые происходят во время ''цикла отображения меню''. Цикл состоит из множества уведомлений:&lt;br /&gt;
*Начало уведомления.&lt;br /&gt;
**Уведомление об отображении, если меню может быть отображено клиенту.&lt;br /&gt;
**Уведомление о выборе пункта или кнопки отмены.&lt;br /&gt;
*Конец уведомления.&lt;br /&gt;
&lt;br /&gt;
Так как ''End'' обозначает конец полного цикла отображения, то обычно его используют для уничтожения временных меню.&lt;br /&gt;
&lt;br /&gt;
==Спецификация==&lt;br /&gt;
Детальное объяснение этих событий ниже. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель всегда доступен. Для SourcePawn, &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; хандл и &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; всегда установлены в &amp;lt;tt&amp;gt;MenuHandler&amp;lt;/tt&amp;gt; callback. В отличии от C++, SourcePawn API позволяет вызвать лишь некоторые действия, если они были запрошены во время создания меню. Это сделано в целях оптимизации. Однако, эти действия не могут быть не вызваны.&lt;br /&gt;
&lt;br /&gt;
*'''Start'''. Меню было признано. Это не означает, что меню будет отображено; однако, это гарантирует, что событие &amp;quot;OnMenuEnd&amp;quot; будет вызвано.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuStart()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
*'''Display'''.  Меню отобразилось клиенту.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuDisplay()&amp;lt;/tt&amp;gt; в C++. &amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; доступны указатель и индекс клиента.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Display&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Хандл меню.&lt;br /&gt;
*'''Select'''. Пункт был выбран. Будет дана позиция пункта в меню, а не нажатая кнопка (если меню не является панелью) &lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и позиция пункта.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Позиция пункта.&lt;br /&gt;
*'''Cancel'''.  Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
*'''End'''. Цикл отображения меню закончился; это означает, что &amp;quot;Start&amp;quot; действие было, и одно из действий &amp;quot;Select&amp;quot; или &amp;quot;Cancel&amp;quot; так же произошло. Обычно в этом месте отчищаются ресурсы меню или само меню удаляется.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuEnd()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Причина завершения меню.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: если param1 == MenuEnd_Cancelled, то здесь содержится код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Для панелей цикл жизни другой. Панели могут вызывать лишь два callback'а из написанных выше, и гарантируется, что один из них точно будет вызван для данного цикла отображения. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель будет всегда &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. Для SourcePawn, меню Handle будет всегда &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*'''Select'''.  Кнопка была нажата. Этой кнопкой может быть любое число. Например, если в панели у вас 2 пункта, то клиент может вызвать это событие, нажав &amp;quot;43&amp;quot;.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и нажатая кнопка.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Номер нажатой клавиши.&lt;br /&gt;
*'''Cancel'''. Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Примеры=&lt;br /&gt;
Для начала попробуем сделать очень простое меню:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы любите яблоки?&lt;br /&gt;
1. Да&lt;br /&gt;
2. Нет&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделаем это как меню и как панель, чтобы увидеть различия в их API.&lt;br /&gt;
&lt;br /&gt;
==Простое меню==&lt;br /&gt;
Сперва напишем наш пример через API Меню. Для пошагового руководства смотрите [[Menus Step By Step (SourceMod Scripting)]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду menu_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// хандл меню&lt;br /&gt;
int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
	/* Если был выбран какой-либо пункт, то сообщим клиенту о его выборе. */&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		char info[32]; // переменная для хранения выбора&lt;br /&gt;
		bool found = GetMenuItem(menu, param2, info, sizeof(info)); // получаем информацию о выбранном в меню пункте&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы нажали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info); // пишем клиенту в консоль&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню было отменено, то сообщим об этом серверу. */&lt;br /&gt;
	else if (action == MenuAction_Cancel)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл меню.  Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню &amp;quot;закончилось&amp;quot;, то удалим его из памяти */&lt;br /&gt;
	else if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		delete menu;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:menu = CreateMenu(MenuHandler1);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;да&amp;quot;, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;нет&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	DisplayMenu(menu, client, 20);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несколько очень важных замечаний о этом примере:&lt;br /&gt;
*Одно из &amp;lt;tt&amp;gt;Select&amp;lt;/tt&amp;gt; или &amp;lt;tt&amp;gt;Cancel&amp;lt;/tt&amp;gt; событий всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*&amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*Мы удаляем наше меню в &amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; действии, потому что наш дескриптор (Handle) нам больше не нужен. Если бы мы удалили его после &amp;lt;tt&amp;gt;DisplayMenu&amp;lt;/tt&amp;gt;, это бы отменило отображение меню клиенту.&lt;br /&gt;
*Меню по умолчанию имеет кнопку выхода. В нашем примере мы его отключили.&lt;br /&gt;
*Наше меню будет отображаться 20 секунд. Это означает, что если клиент ничего не выбрал в течении 20 секунд, то меню будет закрыто. Это обычно необходимо для меню с голосованиями. В отличии от AMX Mod X, вам не нужно создавать таймер, чтобы быть увереным, что меню &amp;quot;закончилось&amp;quot;.&lt;br /&gt;
*Мы создали и уничтожили новый дескриптор меню (Menu Handle), однако мы можем этого и не делать. Вполне допустимо создать дескриптор (Handle) один раз для всего времени жизни плагина.&lt;br /&gt;
*Чтобы нормально отображались русские буквы, кодировка файла должна быть UTF-8 (без BOOM), иначе будут квадратики вместо русских букв.&lt;br /&gt;
&lt;br /&gt;
Наше законченное меню и приложенный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика).&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовая панель==&lt;br /&gt;
Теперь давайте перепишем наш пример с использованием Панели вместо Меню.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду panel_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;panel_test1&amp;quot;, Panel_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public PanelHandler1(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d&amp;quot;, param2);&lt;br /&gt;
	} else if (action == MenuAction_Cancel) {&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл панель. Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Panel_Test1(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:panel = CreatePanel();&lt;br /&gt;
	SetPanelTitle(panel, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Да&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Нет&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
	SendPanelToClient(panel, client, PanelHandler1, 20);&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(panel);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как вы можете заметить, Панели сильно отличаются от Меню:&lt;br /&gt;
*Мы можем удалить Панель как только закончим его отображать. Мы можем создать Панель один раз, и продолжать использовать его много раз, однако мы можем удалить его в любой момент, без нарушения цикла отображения клиентского меню.&lt;br /&gt;
*Обработчик Панели (Handler) получает гораздо меньше информации. Так как Панели разрабатывались как грубый вывод текста, была сохранена возможность добавления информации без занятия слота. Поэтому обработчик может знать лишь об отмене или о нажатии любой клавиши с номером.&lt;br /&gt;
*Отсутствует автоматизация. Вы не можете добавить больше определенного числа выбираемых пунктов и добавить нумерацию. Автоматический контроль требует использования тяжелого объектного API Меню.&lt;br /&gt;
&lt;br /&gt;
Наше законченная панель и приложеный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_panel_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовое меню с нумерацией==&lt;br /&gt;
Теперь сделаем пример с большим функционалом -- нумерацией. Попробуем сделать меню для смены карты. Самый простой путь - прочесть &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл в начале плагина и создать меню по этому файла.&lt;br /&gt;
&lt;br /&gt;
Так как чтение и парсинг файла довольно долгая операция, мы будем делать это только раз за карту. Поэтому мы построим меню в &amp;lt;tt&amp;gt;OnMapStart&amp;lt;/tt&amp;gt;, и мы не будем вызывать &amp;lt;tt&amp;gt;CloseHandle&amp;lt;/tt&amp;gt; до события &amp;lt;tt&amp;gt;OnMapEnd&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:g_MapMenu = INVALID_HANDLE&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_changemap&amp;quot;, Command_ChangeMap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	g_MapMenu = BuildMapMenu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapEnd()&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu != INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		CloseHandle(g_MapMenu);&lt;br /&gt;
		g_MapMenu = INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Handle:BuildMapMenu()&lt;br /&gt;
{&lt;br /&gt;
	/* открываем файл */&lt;br /&gt;
	new Handle:file = OpenFile(&amp;quot;maplist.txt&amp;quot;, &amp;quot;rt&amp;quot;);&lt;br /&gt;
	if (file == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	/* создаем хандл меню */&lt;br /&gt;
	new Handle:menu = CreateMenu(Menu_ChangeMap);&lt;br /&gt;
	new String:mapname[255];&lt;br /&gt;
	while (!IsEndOfFile(file) &amp;amp;&amp;amp; ReadFileLine(file, mapname, sizeof(mapname)))&lt;br /&gt;
	{&lt;br /&gt;
		if (mapname[0] == ';' || !IsCharAlpha(mapname[0]))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* выбрасываем все пробелы из названия */&lt;br /&gt;
		new len = strlen(mapname);&lt;br /&gt;
		for (new i=0; i&amp;lt;len; i++)&lt;br /&gt;
		{&lt;br /&gt;
			if (IsCharSpace(mapname[i]))&lt;br /&gt;
			{&lt;br /&gt;
				mapname[i] = '\0';&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		/* проверяем карту на валидность */&lt;br /&gt;
		if (!IsMapValid(mapname))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* добавляем карту в меню */&lt;br /&gt;
		AddMenuItem(menu, mapname, mapname);&lt;br /&gt;
	}&lt;br /&gt;
	/* не забываем закрыть файл! */&lt;br /&gt;
	CloseHandle(file);&lt;br /&gt;
	&lt;br /&gt;
	/* наконец, устанавливаем заглавие меню */&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Выберите карту:&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return menu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Menu_ChangeMap(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		new String:info[32];&lt;br /&gt;
&lt;br /&gt;
		/* получаем информацию о пункте */&lt;br /&gt;
		new bool:found = GetMenuItem(menu, param2, info, sizeof(info));&lt;br /&gt;
&lt;br /&gt;
		/* говорим об этом клиенту */&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
&lt;br /&gt;
		/* меняем карту */&lt;br /&gt;
		ServerCommand(&amp;quot;changelevel %s&amp;quot;, info);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_ChangeMap(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Файл maplist.txt не был найден!&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}	&lt;br /&gt;
	&lt;br /&gt;
	DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результатом такого меню будет множество пунктов (&amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл содержал примерно 18 карт). Таким образом, конечное меню содержит три страницы, которые идут друг за другом, и выглядят вот так:&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_2_page1.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page2.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page3.PNG]]&lt;br /&gt;
&lt;br /&gt;
Сообщение в консоль будет выведено до смены карты, например, если выбрали &amp;lt;tt&amp;gt;cs_office&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы выбрали: 8 (найдено? 1 информация: cs_office)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отображение и разработка этого меню через &amp;lt;tt&amp;gt;ShowMenu&amp;lt;/tt&amp;gt; сообщение или &amp;lt;tt&amp;gt;Панельный&amp;lt;/tt&amp;gt; API занимает много времени и является довольно сложной задачей. Придется отслеживать все пункты в массиве большого размера, страницы, которые пользователь просматривает, и написать функцию, которая будет вычислять, на основе текущей странице, пункт, который был выбран и какую кнопку нажали. Меню системы это всё делает за вас.&lt;br /&gt;
&lt;br /&gt;
'''Заметки:'''&lt;br /&gt;
*Пункты управления, которые не доступны, не рисуются. Например, на первой странице вы не можете нажать &amp;quot;назад&amp;quot;, а на последней странице вы не можете нажать &amp;quot;вперед&amp;quot;. Несмотря на это, меню пытается удерживать каждый интерфейс последовательно. Таким образом, визуально, каждая навигационная клавиша всегда на одной и той же позиции.&lt;br /&gt;
*Если указать таймаут меню, то переключение между страницами не воздействует на общее время открытия меню. Например, если мы установим таймаут на 20 секунд, то каждое листание страницы будет устанавливать таймайт равный (20 - время, уже затраченное на просмотр). Только переоткрытие меню позволит вернуть таймаут времени на 20 секунд.&lt;br /&gt;
*Если отключить кнопку выхода, то не смотря на это, пункты 8 и 9 будут всё так же &amp;quot;назад&amp;quot; и вперед, соответственно.&lt;br /&gt;
*Мы не освобождаем дескриптор (Handle) меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, потому что наше меню глобальное/статическое, и нам не нужно пересоздавать его каждый раз.&lt;br /&gt;
*На изображении отображена кнопка &amp;quot;Назад&amp;quot;. В SourceMod версии 1011 и выше, кнопка &amp;quot;Назад&amp;quot; была переименована в &amp;quot;Предыдующая&amp;quot;, и &amp;quot;Назад&amp;quot; зарезервированно для &amp;quot;ExitBack&amp;quot; функциональности. (актуально для ENG версии, на ру вроде бы оба пункта &amp;quot;назад&amp;quot;. прим. переводчик)&lt;br /&gt;
&lt;br /&gt;
=Голосование=&lt;br /&gt;
SourceMod также имеет API для отображения меню как голосования для больше чем одного клиента. SourceMod автоматически обрабатываем выбранные пункты and randomly picking a tie-breaker (не знаю как перевести. прим. переводчика). Так же API голосования добавляет два новых значения для &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt;, которые во время отображения меню всегда вызываются.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteStart&amp;lt;/tt&amp;gt;: Вызывается после &amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt;, когда голосование оффициально началось.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;: Вызывается после того, как все клиенты проголосовали или отменили свои меню голосований. Выбранный предмет может быть получен через &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. Вызывается '''перед''' &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. Важно заметить, что это не замена &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, это разные вещи. Нельзя освобождать меню в &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;. '''Заметка:''' не вызывается, если используется &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteCancel&amp;lt;/tt&amp;gt;: Вызывается, если меню было отмеено в процессе голосования. Если вызвалось, то &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; или обратный вызов (callback) для результата голосований не будет вызваны, но &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; будет после этого.  Причина отмены голосования находится в &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Система меню - это то же самое меню, только с двумя дополнительными свойствами:&lt;br /&gt;
*Только одно голосование может быть активно. Вы должны проверять, не запущено ли голосование (&amp;lt;tt&amp;gt;IsVoteInProgress&amp;lt;/tt&amp;gt;()), иначе &amp;lt;tt&amp;gt;VoteMenu&amp;lt;/tt&amp;gt;() не сработает.&lt;br /&gt;
*Если клиент во время голосования отключится, то его голос будет недействительным. &lt;br /&gt;
&lt;br /&gt;
Пример внизу показывает, как создать функцию &amp;lt;tt&amp;gt;DoVoteMenu()&amp;lt;/tt&amp;gt;, которая будет спрашивать, хотят ли они сменить карту на предложенную.&lt;br /&gt;
&lt;br /&gt;
==Простое Голосование==&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Продвинутое Голосование==&lt;br /&gt;
Если вам нужно больше информации о результатах голосовании, чем вам дает &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;, вы можете указать отдельный обратный вызов (callback). Новый обратный вызов будет давать больше информации, но за опреденную цену: &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; не будет вызвано, и вам придется самим решать, как обработать результат. Это делается с помощью &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Handle_VoteResults(Handle:menu, &lt;br /&gt;
			num_votes, &lt;br /&gt;
			num_clients, &lt;br /&gt;
			const client_info[][2], &lt;br /&gt;
			num_items, &lt;br /&gt;
			const item_info[][2])&lt;br /&gt;
{&lt;br /&gt;
	/* Проверяем, нет ли сразу двух победителей */&lt;br /&gt;
	new winner = 0;&lt;br /&gt;
	if (num_items &amp;gt; 1&lt;br /&gt;
	    &amp;amp;&amp;amp; (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))&lt;br /&gt;
	{&lt;br /&gt;
		winner = GetRandomInt(0, 1);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	new String:map[64];&lt;br /&gt;
	GetMenuItem(menu, item_info[winner][VOTEINFO_ITEM_INDEX], map, sizeof(map));&lt;br /&gt;
	ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetVoteResultCallback(menu, Handle_VoteResults);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=ExitBack=&lt;br /&gt;
ExitBack - специальный термин для обозначения &amp;quot;ExitBack Кнопки&amp;quot;. По умолчанию эта кнопка отключена.  Обычно нумерованные меню не имеют пункта &amp;quot;Назад&amp;quot; для первой страници. Если &amp;quot;ExitBack&amp;quot; кнопка включена, появится кнопка &amp;quot;Назад&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
Выбор опции &amp;quot;ExitBack&amp;quot; для меню в обратном вызове будет в действии &amp;lt;tt&amp;gt;MenuCancel_ExitBack&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;MenuEnd_ExitBack&amp;lt;/tt&amp;gt;. Функциональность этого такая же, как и у простого выхода из меню; дополнительная функциональность должна быть определена через обратный вызов.&lt;br /&gt;
&lt;br /&gt;
=Освобождение Дескрипторов (Handle) меню=&lt;br /&gt;
Важно закрывать дескриптор меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; вызывается когда меню закрылось и больше не нужно.&lt;br /&gt;
&lt;br /&gt;
=Переводы=&lt;br /&gt;
Есть возможность динамически переводить меню для каждого игрока через &amp;lt;tt&amp;gt;MenuAction_DisplayItem&amp;lt;/tt&amp;gt; callback. Специальный натив, &amp;lt;tt&amp;gt;RedrawMenuItem&amp;lt;/tt&amp;gt;, используется чтобы преобразовать текст внутри вызова. Давайте переделаем наш пример голосования с переводами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	} else if (action == MenuAction_DisplayItem) {&lt;br /&gt;
		/* Получаем отображаемую строку, будем использовать как фразу перевода */&lt;br /&gt;
		decl String:display[64];&lt;br /&gt;
		GetMenuItem(menu, param2, &amp;quot;&amp;quot;, 0, _, display, sizeof(display));&lt;br /&gt;
&lt;br /&gt;
		/* Переводим строку на язык клиента */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, display, param1);&lt;br /&gt;
&lt;br /&gt;
		/* Перерисовываем текст */&lt;br /&gt;
		return RedrawMenuItem(buffer);&lt;br /&gt;
	} else if (action == MenuAction_Display) {&lt;br /&gt;
		/* Дескриптор панели будет вторым параметром */&lt;br /&gt;
		new Handle:panel = Handle:param2;&lt;br /&gt;
		&lt;br /&gt;
		/* Получаем название карты, на которую меняем, из первого пункта */&lt;br /&gt;
		decl String:map[64];&lt;br /&gt;
		GetMenuItem(menu, 0, map, sizeof(map));&lt;br /&gt;
		&lt;br /&gt;
		/* Переводим нашу фразу */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Change map to?&amp;quot;, client, map);&lt;br /&gt;
&lt;br /&gt;
		SetPanelTitle(panel, buffer);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu, MenuAction_DisplayItem|MenuAction_Display);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)/ru&amp;diff=11209</id>
		<title>Menu API (SourceMod)/ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)/ru&amp;diff=11209"/>
		<updated>2021-07-20T13:38:17Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Kleiner moved page Menu API (SourceMod)/ru to Ru:Menu API (SourceMod) over redirect: sorry&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Ru:Menu API (SourceMod)]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11207</id>
		<title>Menu API (SourceMod)=ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11207"/>
		<updated>2021-07-20T13:37:53Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Menu API (SourceMod)}}&lt;br /&gt;
SourceMod обладает большим API для создания и отображений меню для клиентов. В отличии от AMX Mod X, API SourceMod'a обладает широким функционалом. Меню базируются на обратных вызовах (callbacks), которые гарантированно будут вызваны.&lt;br /&gt;
&lt;br /&gt;
Для C++, API меню может быть найдено в &amp;lt;tt&amp;gt;public/IMenuManager.h&amp;lt;/tt&amp;gt;. Для SourcePawn - &amp;lt;tt&amp;gt;scripting/include/menus.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Объекты=&lt;br /&gt;
Система меню SourceMod основана на объектно ориентированной иерархии. Понимание этой иерархии, даже для скриптинга, является ключевым моментом для эффективного использования меню.&lt;br /&gt;
&lt;br /&gt;
==Стили==&lt;br /&gt;
Высший уровень объектов называется ''MenuStyle'' (&amp;lt;tt&amp;gt;IMenuStyle&amp;lt;/tt&amp;gt; в C++). Стили описывают уникальную систему меню. Всего есть два стиля, встроенных в SourceMod:&lt;br /&gt;
*Valve Styly, так же называемым &amp;quot;ESC&amp;quot; меню; 8 пунктов на странице, нельзя добавить текст без занятия слота или спрятать (отключить) текст.&lt;br /&gt;
*Radio Style, так же называемый &amp;quot;AMX&amp;quot; меню; 10 пунктов на странице, можно добавить любой текст.&lt;br /&gt;
&lt;br /&gt;
Каждый MenuStyle обладает своими правилами и свойствами. Вы можете считать, что каждый из них существует на раздельных &amp;quot;каналах&amp;quot;. Например, два разных меню могут быть на экране игрока, будучи Valve и Radio меню одновременно, и SourceMod сможет обработать их обоих без всяких проблем. Это всё возможно благодаря тому, что каждый стиль отслеживает свои меню отдельно.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Меню низшего уровня называется ''Панелью'' (&amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; в C++). Панель - один ''''кусок'''' текста. Пункты и текст могут добавляться к панели столько раз, сколько поддерживает родительский стиль. Например, Valve стиль не поддерживает отображение текста без занятия слота или отключенных пунктов. Однако с Radio стилем панели это становится возможным, и вы можете отобразить большое количество текста так, как вам это захочется. &lt;br /&gt;
&lt;br /&gt;
Панель считается временным объектом. Она создается, рендерится, отображается, и затем удаляется. Хотя она может быть сохранена на неопределенное время, если в этом есть необходимость.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Vavle стиля:&lt;br /&gt;
*Максимум пунктов на странице: 8&lt;br /&gt;
*Отключенный текст не отображается.&lt;br /&gt;
*Нельзя написать текст, не заняв слота.&lt;br /&gt;
*Пробелы не добавляются к пробелам\новым линиям, добавляя чувство &amp;quot;ограниченности&amp;quot;. &lt;br /&gt;
*Пользователь должен нажать &amp;quot;ESC&amp;quot; или быть в своей консоли, чтобы увидеть меню.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Radio стиля:&lt;br /&gt;
*10 пунктов на странице&lt;br /&gt;
*Заглавия белые; пункты желтые, если не отключены. Если отключены, то белого цвета.&lt;br /&gt;
*Нулевой элемент всегда белый. Для согласованности это значит, что навигационное управление всегда белое и находится в следующей секции, и просто не отрисовывается, если отсутствует.&lt;br /&gt;
&lt;br /&gt;
==Меню==&lt;br /&gt;
Наконец, есть просто ''Меню'' (&amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; in C++). Это вспомогательный объект, созданный для хранения базированных на меню выбираемых пунктов. В отличии от низкоуровневых панелей, меню содержит '''пункты''', и может содержать только те пункты, которые можно выбрать (т.е. занимающие слоты). Они делятся на две категории:&lt;br /&gt;
*Не нумерованные: эти меню могут иметь определенное число пунктов, и не имеют навигационных опций, кроме кнопки &amp;quot;Выход&amp;quot;, которая всегда добавляется на последнюю позицию, поддерживаемую стилем.&lt;br /&gt;
**Valve Стиль: максимум пунктов: 8&lt;br /&gt;
**Radio Стиль: максимум пунктов: 10&lt;br /&gt;
*Нумерованные: такие меню могут содержать сколь угодно пунктов. Во время отображения будет показано только определенное число пунктов. Автоматически будут добавлены навигационные пункты, таким образом, игроки легко могут возвращаться назад и вперед к определенным &amp;quot;страницам&amp;quot; пунктов меню.&lt;br /&gt;
**&amp;quot;Назад&amp;quot; всегда отображается как первый навигационный пункт, третий с конца поддерживаемых позиций. Не будет добавлен, если меню содержит только одну страницу. Если нет никаких предыдущих страниц, текст никогда не будет отображен, ни в каком стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 6&lt;br /&gt;
***Radio Стиль, позиция: 8&lt;br /&gt;
**&amp;quot;Вперед&amp;quot; всегда отображается как второй навигационный пункт, второй с конца из доступных позиций. Не отображается, если меню состоит из одной страницы. Если нет следующих страниц, то текс не будет отображен при любом стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 7&lt;br /&gt;
***Radio Стиль, позиция: 9&lt;br /&gt;
**&amp;quot;Выход&amp;quot; отображается, если у меню включена кнопка &amp;quot;выход&amp;quot;. Всегда занимает последнюю из возможных позиций&lt;br /&gt;
***Valve Стиль, позиция: 8&lt;br /&gt;
***Radio Стиль, позиция: 10&lt;br /&gt;
&lt;br /&gt;
Цель меню - упростить процедуру хранения, отображения, и вычисления выбранного пункта. Таким образом, меню не позволяет добавлять пустой текст, так как это значительно усложняет алгоритм отображения.  &lt;br /&gt;
&lt;br /&gt;
Внутренне, меню отображается через ''RenderMenu'' алогритм. Этот алгоритм создает временную панель и заполняет её пунктами из меню. Эта панель отображается клиенту. Алгоритм пытается создать свободное перемещение по всем меню, и по всем стилям. Таким образом, все меню отображаются через &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; класс, или &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; дескрипторы (Handles), и будет выглядеть и действовать так же, как и меню API, основанное на панельном API.&lt;br /&gt;
&lt;br /&gt;
=Обратные вызовы (callbacks)=&lt;br /&gt;
==Краткий обзор==&lt;br /&gt;
Меню базируются на системе обратных вызовов. Каждый обратный вызов предоставляет собой действие, которые происходят во время ''цикла отображения меню''. Цикл состоит из множества уведомлений:&lt;br /&gt;
*Начало уведомления.&lt;br /&gt;
**Уведомление об отображении, если меню может быть отображено клиенту.&lt;br /&gt;
**Уведомление о выборе пункта или кнопки отмены.&lt;br /&gt;
*Конец уведомления.&lt;br /&gt;
&lt;br /&gt;
Так как ''End'' обозначает конец полного цикла отображения, то обычно его используют для уничтожения временных меню.&lt;br /&gt;
&lt;br /&gt;
==Спецификация==&lt;br /&gt;
Детальное объяснение этих событий ниже. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель всегда доступен. Для SourcePawn, &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; хандл и &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; всегда установлены в &amp;lt;tt&amp;gt;MenuHandler&amp;lt;/tt&amp;gt; callback. В отличии от C++, SourcePawn API позволяет вызвать лишь некоторые действия, если они были запрошены во время создания меню. Это сделано в целях оптимизации. Однако, эти действия не могут быть не вызваны.&lt;br /&gt;
&lt;br /&gt;
*'''Start'''. Меню было признано. Это не означает, что меню будет отображено; однако, это гарантирует, что событие &amp;quot;OnMenuEnd&amp;quot; будет вызвано.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuStart()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
*'''Display'''.  Меню отобразилось клиенту.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuDisplay()&amp;lt;/tt&amp;gt; в C++. &amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; доступны указатель и индекс клиента.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Display&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Хандл меню.&lt;br /&gt;
*'''Select'''. Пункт был выбран. Будет дана позиция пункта в меню, а не нажатая кнопка (если меню не является панелью) &lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и позиция пункта.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Позиция пункта.&lt;br /&gt;
*'''Cancel'''.  Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
*'''End'''. Цикл отображения меню закончился; это означает, что &amp;quot;Start&amp;quot; действие было, и одно из действий &amp;quot;Select&amp;quot; или &amp;quot;Cancel&amp;quot; так же произошло. Обычно в этом месте отчищаются ресурсы меню или само меню удаляется.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuEnd()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Причина завершения меню.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: если param1 == MenuEnd_Cancelled, то здесь содержится код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Для панелей цикл жизни другой. Панели могут вызывать лишь два callback'а из написанных выше, и гарантируется, что один из них точно будет вызван для данного цикла отображения. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель будет всегда &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. Для SourcePawn, меню Handle будет всегда &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*'''Select'''.  Кнопка была нажата. Этой кнопкой может быть любое число. Например, если в панели у вас 2 пункта, то клиент может вызвать это событие, нажав &amp;quot;43&amp;quot;.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и нажатая кнопка.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Номер нажатой клавиши.&lt;br /&gt;
*'''Cancel'''. Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Примеры=&lt;br /&gt;
Для начала попробуем сделать очень простое меню:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы любите яблоки?&lt;br /&gt;
1. Да&lt;br /&gt;
2. Нет&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделаем это как меню и как панель, чтобы увидеть различия в их API.&lt;br /&gt;
&lt;br /&gt;
==Простое меню==&lt;br /&gt;
Сперва напишем наш пример через API Меню. Для пошагового руководства смотрите [[Menus Step By Step (SourceMod Scripting)]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду menu_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// хандл меню&lt;br /&gt;
int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
	/* Если был выбран какой-либо пункт, то сообщим клиенту о его выборе. */&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		char info[32]; // переменная для хранения выбора&lt;br /&gt;
		bool found = GetMenuItem(menu, param2, info, sizeof(info)); // получаем информацию о выбранном в меню пункте&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы нажали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info); // пишем клиенту в консоль&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню было отменено, то сообщим об этом серверу. */&lt;br /&gt;
	else if (action == MenuAction_Cancel)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл меню.  Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню &amp;quot;закончилось&amp;quot;, то удалим его из памяти */&lt;br /&gt;
	else if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		delete menu;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:menu = CreateMenu(MenuHandler1);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;да&amp;quot;, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;нет&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	DisplayMenu(menu, client, 20);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несколько очень важных замечаний о этом примере:&lt;br /&gt;
*Одно из &amp;lt;tt&amp;gt;Select&amp;lt;/tt&amp;gt; или &amp;lt;tt&amp;gt;Cancel&amp;lt;/tt&amp;gt; событий всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*&amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*Мы удаляем наше меню в &amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; действии, потому что наш дескриптор (Handle) нам больше не нужен. Если бы мы удалили его после &amp;lt;tt&amp;gt;DisplayMenu&amp;lt;/tt&amp;gt;, это бы отменило отображение меню клиенту.&lt;br /&gt;
*Меню по умолчанию имеет кнопку выхода. В нашем примере мы его отключили.&lt;br /&gt;
*Наше меню будет отображаться 20 секунд. Это означает, что если клиент ничего не выбрал в течении 20 секунд, то меню будет закрыто. Это обычно необходимо для меню с голосованиями. В отличии от AMX Mod X, вам не нужно создавать таймер, чтобы быть увереным, что меню &amp;quot;закончилось&amp;quot;.&lt;br /&gt;
*Мы создали и уничтожили новый дескриптор меню (Menu Handle), однако мы можем этого и не делать. Вполне допустимо создать дескриптор (Handle) один раз для всего времени жизни плагина.&lt;br /&gt;
*Чтобы нормально отображались русские буквы, кодировка файла должна быть UTF-8 (без BOOM), иначе будут квадратики вместо русских букв.&lt;br /&gt;
&lt;br /&gt;
Наше законченное меню и приложенный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика).&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовая панель==&lt;br /&gt;
Теперь давайте перепишем наш пример с использованием Панели вместо Меню.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду panel_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;panel_test1&amp;quot;, Panel_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public PanelHandler1(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d&amp;quot;, param2);&lt;br /&gt;
	} else if (action == MenuAction_Cancel) {&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл панель. Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Panel_Test1(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:panel = CreatePanel();&lt;br /&gt;
	SetPanelTitle(panel, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Да&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Нет&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
	SendPanelToClient(panel, client, PanelHandler1, 20);&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(panel);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как вы можете заметить, Панели сильно отличаются от Меню:&lt;br /&gt;
*Мы можем удалить Панель как только закончим его отображать. Мы можем создать Панель один раз, и продолжать использовать его много раз, однако мы можем удалить его в любой момент, без нарушения цикла отображения клиентского меню.&lt;br /&gt;
*Обработчик Панели (Handler) получает гораздо меньше информации. Так как Панели разрабатывались как грубый вывод текста, была сохранена возможность добавления информации без занятия слота. Поэтому обработчик может знать лишь об отмене или о нажатии любой клавиши с номером.&lt;br /&gt;
*Отсутствует автоматизация. Вы не можете добавить больше определенного числа выбираемых пунктов и добавить нумерацию. Автоматический контроль требует использования тяжелого объектного API Меню.&lt;br /&gt;
&lt;br /&gt;
Наше законченная панель и приложеный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_panel_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовое меню с нумерацией==&lt;br /&gt;
Теперь сделаем пример с большим функционалом -- нумерацией. Попробуем сделать меню для смены карты. Самый простой путь - прочесть &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл в начале плагина и создать меню по этому файла.&lt;br /&gt;
&lt;br /&gt;
Так как чтение и парсинг файла довольно долгая операция, мы будем делать это только раз за карту. Поэтому мы построим меню в &amp;lt;tt&amp;gt;OnMapStart&amp;lt;/tt&amp;gt;, и мы не будем вызывать &amp;lt;tt&amp;gt;CloseHandle&amp;lt;/tt&amp;gt; до события &amp;lt;tt&amp;gt;OnMapEnd&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:g_MapMenu = INVALID_HANDLE&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_changemap&amp;quot;, Command_ChangeMap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	g_MapMenu = BuildMapMenu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapEnd()&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu != INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		CloseHandle(g_MapMenu);&lt;br /&gt;
		g_MapMenu = INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Handle:BuildMapMenu()&lt;br /&gt;
{&lt;br /&gt;
	/* открываем файл */&lt;br /&gt;
	new Handle:file = OpenFile(&amp;quot;maplist.txt&amp;quot;, &amp;quot;rt&amp;quot;);&lt;br /&gt;
	if (file == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	/* создаем хандл меню */&lt;br /&gt;
	new Handle:menu = CreateMenu(Menu_ChangeMap);&lt;br /&gt;
	new String:mapname[255];&lt;br /&gt;
	while (!IsEndOfFile(file) &amp;amp;&amp;amp; ReadFileLine(file, mapname, sizeof(mapname)))&lt;br /&gt;
	{&lt;br /&gt;
		if (mapname[0] == ';' || !IsCharAlpha(mapname[0]))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* выбрасываем все пробелы из названия */&lt;br /&gt;
		new len = strlen(mapname);&lt;br /&gt;
		for (new i=0; i&amp;lt;len; i++)&lt;br /&gt;
		{&lt;br /&gt;
			if (IsCharSpace(mapname[i]))&lt;br /&gt;
			{&lt;br /&gt;
				mapname[i] = '\0';&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		/* проверяем карту на валидность */&lt;br /&gt;
		if (!IsMapValid(mapname))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* добавляем карту в меню */&lt;br /&gt;
		AddMenuItem(menu, mapname, mapname);&lt;br /&gt;
	}&lt;br /&gt;
	/* не забываем закрыть файл! */&lt;br /&gt;
	CloseHandle(file);&lt;br /&gt;
	&lt;br /&gt;
	/* наконец, устанавливаем заглавие меню */&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Выберите карту:&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return menu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Menu_ChangeMap(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		new String:info[32];&lt;br /&gt;
&lt;br /&gt;
		/* получаем информацию о пункте */&lt;br /&gt;
		new bool:found = GetMenuItem(menu, param2, info, sizeof(info));&lt;br /&gt;
&lt;br /&gt;
		/* говорим об этом клиенту */&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
&lt;br /&gt;
		/* меняем карту */&lt;br /&gt;
		ServerCommand(&amp;quot;changelevel %s&amp;quot;, info);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_ChangeMap(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Файл maplist.txt не был найден!&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}	&lt;br /&gt;
	&lt;br /&gt;
	DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результатом такого меню будет множество пунктов (&amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл содержал примерно 18 карт). Таким образом, конечное меню содержит три страницы, которые идут друг за другом, и выглядят вот так:&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_2_page1.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page2.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page3.PNG]]&lt;br /&gt;
&lt;br /&gt;
Сообщение в консоль будет выведено до смены карты, например, если выбрали &amp;lt;tt&amp;gt;cs_office&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы выбрали: 8 (найдено? 1 информация: cs_office)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отображение и разработка этого меню через &amp;lt;tt&amp;gt;ShowMenu&amp;lt;/tt&amp;gt; сообщение или &amp;lt;tt&amp;gt;Панельный&amp;lt;/tt&amp;gt; API занимает много времени и является довольно сложной задачей. Придется отслеживать все пункты в массиве большого размера, страницы, которые пользователь просматривает, и написать функцию, которая будет вычислять, на основе текущей странице, пункт, который был выбран и какую кнопку нажали. Меню системы это всё делает за вас.&lt;br /&gt;
&lt;br /&gt;
'''Заметки:'''&lt;br /&gt;
*Пункты управления, которые не доступны, не рисуются. Например, на первой странице вы не можете нажать &amp;quot;назад&amp;quot;, а на последней странице вы не можете нажать &amp;quot;вперед&amp;quot;. Несмотря на это, меню пытается удерживать каждый интерфейс последовательно. Таким образом, визуально, каждая навигационная клавиша всегда на одной и той же позиции.&lt;br /&gt;
*Если указать таймаут меню, то переключение между страницами не воздействует на общее время открытия меню. Например, если мы установим таймаут на 20 секунд, то каждое листание страницы будет устанавливать таймайт равный (20 - время, уже затраченное на просмотр). Только переоткрытие меню позволит вернуть таймаут времени на 20 секунд.&lt;br /&gt;
*Если отключить кнопку выхода, то не смотря на это, пункты 8 и 9 будут всё так же &amp;quot;назад&amp;quot; и вперед, соответственно.&lt;br /&gt;
*Мы не освобождаем дескриптор (Handle) меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, потому что наше меню глобальное/статическое, и нам не нужно пересоздавать его каждый раз.&lt;br /&gt;
*На изображении отображена кнопка &amp;quot;Назад&amp;quot;. В SourceMod версии 1011 и выше, кнопка &amp;quot;Назад&amp;quot; была переименована в &amp;quot;Предыдующая&amp;quot;, и &amp;quot;Назад&amp;quot; зарезервированно для &amp;quot;ExitBack&amp;quot; функциональности. (актуально для ENG версии, на ру вроде бы оба пункта &amp;quot;назад&amp;quot;. прим. переводчик)&lt;br /&gt;
&lt;br /&gt;
=Голосование=&lt;br /&gt;
SourceMod также имеет API для отображения меню как голосования для больше чем одного клиента. SourceMod автоматически обрабатываем выбранные пункты and randomly picking a tie-breaker (не знаю как перевести. прим. переводчика). Так же API голосования добавляет два новых значения для &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt;, которые во время отображения меню всегда вызываются.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteStart&amp;lt;/tt&amp;gt;: Вызывается после &amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt;, когда голосование оффициально началось.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;: Вызывается после того, как все клиенты проголосовали или отменили свои меню голосований. Выбранный предмет может быть получен через &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. Вызывается '''перед''' &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. Важно заметить, что это не замена &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, это разные вещи. Нельзя освобождать меню в &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;. '''Заметка:''' не вызывается, если используется &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteCancel&amp;lt;/tt&amp;gt;: Вызывается, если меню было отмеено в процессе голосования. Если вызвалось, то &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; или обратный вызов (callback) для результата голосований не будет вызваны, но &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; будет после этого.  Причина отмены голосования находится в &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Система меню - это то же самое меню, только с двумя дополнительными свойствами:&lt;br /&gt;
*Только одно голосование может быть активно. Вы должны проверять, не запущено ли голосование (&amp;lt;tt&amp;gt;IsVoteInProgress&amp;lt;/tt&amp;gt;()), иначе &amp;lt;tt&amp;gt;VoteMenu&amp;lt;/tt&amp;gt;() не сработает.&lt;br /&gt;
*Если клиент во время голосования отключится, то его голос будет недействительным. &lt;br /&gt;
&lt;br /&gt;
Пример внизу показывает, как создать функцию &amp;lt;tt&amp;gt;DoVoteMenu()&amp;lt;/tt&amp;gt;, которая будет спрашивать, хотят ли они сменить карту на предложенную.&lt;br /&gt;
&lt;br /&gt;
==Простое Голосование==&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Продвинутое Голосование==&lt;br /&gt;
Если вам нужно больше информации о результатах голосовании, чем вам дает &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;, вы можете указать отдельный обратный вызов (callback). Новый обратный вызов будет давать больше информации, но за опреденную цену: &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; не будет вызвано, и вам придется самим решать, как обработать результат. Это делается с помощью &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Handle_VoteResults(Handle:menu, &lt;br /&gt;
			num_votes, &lt;br /&gt;
			num_clients, &lt;br /&gt;
			const client_info[][2], &lt;br /&gt;
			num_items, &lt;br /&gt;
			const item_info[][2])&lt;br /&gt;
{&lt;br /&gt;
	/* Проверяем, нет ли сразу двух победителей */&lt;br /&gt;
	new winner = 0;&lt;br /&gt;
	if (num_items &amp;gt; 1&lt;br /&gt;
	    &amp;amp;&amp;amp; (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))&lt;br /&gt;
	{&lt;br /&gt;
		winner = GetRandomInt(0, 1);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	new String:map[64];&lt;br /&gt;
	GetMenuItem(menu, item_info[winner][VOTEINFO_ITEM_INDEX], map, sizeof(map));&lt;br /&gt;
	ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetVoteResultCallback(menu, Handle_VoteResults);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=ExitBack=&lt;br /&gt;
ExitBack - специальный термин для обозначения &amp;quot;ExitBack Кнопки&amp;quot;. По умолчанию эта кнопка отключена.  Обычно нумерованные меню не имеют пункта &amp;quot;Назад&amp;quot; для первой страници. Если &amp;quot;ExitBack&amp;quot; кнопка включена, появится кнопка &amp;quot;Назад&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
Выбор опции &amp;quot;ExitBack&amp;quot; для меню в обратном вызове будет в действии &amp;lt;tt&amp;gt;MenuCancel_ExitBack&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;MenuEnd_ExitBack&amp;lt;/tt&amp;gt;. Функциональность этого такая же, как и у простого выхода из меню; дополнительная функциональность должна быть определена через обратный вызов.&lt;br /&gt;
&lt;br /&gt;
=Освобождение Дескрипторов (Handle) меню=&lt;br /&gt;
Важно закрывать дескриптор меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; вызывается когда меню закрылось и больше не нужно.&lt;br /&gt;
&lt;br /&gt;
=Переводы=&lt;br /&gt;
Есть возможность динамически переводить меню для каждого игрока через &amp;lt;tt&amp;gt;MenuAction_DisplayItem&amp;lt;/tt&amp;gt; callback. Специальный натив, &amp;lt;tt&amp;gt;RedrawMenuItem&amp;lt;/tt&amp;gt;, используется чтобы преобразовать текст внутри вызова. Давайте переделаем наш пример голосования с переводами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	} else if (action == MenuAction_DisplayItem) {&lt;br /&gt;
		/* Получаем отображаемую строку, будем использовать как фразу перевода */&lt;br /&gt;
		decl String:display[64];&lt;br /&gt;
		GetMenuItem(menu, param2, &amp;quot;&amp;quot;, 0, _, display, sizeof(display));&lt;br /&gt;
&lt;br /&gt;
		/* Переводим строку на язык клиента */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, display, param1);&lt;br /&gt;
&lt;br /&gt;
		/* Перерисовываем текст */&lt;br /&gt;
		return RedrawMenuItem(buffer);&lt;br /&gt;
	} else if (action == MenuAction_Display) {&lt;br /&gt;
		/* Дескриптор панели будет вторым параметром */&lt;br /&gt;
		new Handle:panel = Handle:param2;&lt;br /&gt;
		&lt;br /&gt;
		/* Получаем название карты, на которую меняем, из первого пункта */&lt;br /&gt;
		decl String:map[64];&lt;br /&gt;
		GetMenuItem(menu, 0, map, sizeof(map));&lt;br /&gt;
		&lt;br /&gt;
		/* Переводим нашу фразу */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Change map to?&amp;quot;, client, map);&lt;br /&gt;
&lt;br /&gt;
		SetPanelTitle(panel, buffer);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu, MenuAction_DisplayItem|MenuAction_Display);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)&amp;diff=11206</id>
		<title>Menu API (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)&amp;diff=11206"/>
		<updated>2021-07-20T13:35:23Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Menu API (SourceMod)}}&lt;br /&gt;
SourceMod has an extensive API for building and displaying menus to clients.  Unlike AMX Mod X, this API is highly state driven.  Menus are based on callbacks which are guaranteed to be fired.&lt;br /&gt;
&lt;br /&gt;
For C++, the Menu API can be found in &amp;lt;tt&amp;gt;public/IMenuManager.h&amp;lt;/tt&amp;gt;.  For SourcePawn, it is in &amp;lt;tt&amp;gt;scripting/include/menus.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Objects=&lt;br /&gt;
The SourceMod Menu System is based on an object oriented hierarchy.  Understanding this hierarchy, even for scripting, is critical to using menus effectively.&lt;br /&gt;
&lt;br /&gt;
==Styles==&lt;br /&gt;
The top level object is a ''MenuStyle'' (&amp;lt;tt&amp;gt;IMenuStyle&amp;lt;/tt&amp;gt; in C++).  Styles describe a unique menu system.  There are two such styles built into SourceMod:&lt;br /&gt;
*Valve Style, also called &amp;quot;ESC&amp;quot; menus; 8 items per page, no raw/disabled text can be rendered&lt;br /&gt;
*Radio Style, also called &amp;quot;AMX&amp;quot; menus; 10 items per page, raw/disabled text can be rendered&lt;br /&gt;
&lt;br /&gt;
Each MenuStyle has its own rules and properties.  You can think of them as existing on separate &amp;quot;channels.&amp;quot;  For example, two different menus can exist on a player's screen as both a Valve menu and a Radio menu at the same time, and SourceMod will be able to manage both without any problems.  This is because each style keeps track of its own menus separately.&lt;br /&gt;
&lt;br /&gt;
==Panels==&lt;br /&gt;
Menu displays are drawn with a lower level interface called ''Panels'' (&amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; in C++).  Panels describe exactly one chunk of display text.  Both selectable items and raw text can be added to a panel as long as its parent style supports the contents you're trying to draw.  For example, the Valve style does not support drawing raw text or disabled items.  But with a Radio-style Panel, you can display a large amount of on-screen data in your own format.&lt;br /&gt;
&lt;br /&gt;
Panels are considered temporary objects.  They are created, rendered, displayed, and destroyed.  Although they can be saved indefinitely, it is not necessary to do so.&lt;br /&gt;
&lt;br /&gt;
Valve Style drawing rules/limitations:&lt;br /&gt;
*Max items per page is 8.&lt;br /&gt;
*Disabled items cannot be drawn.&lt;br /&gt;
*Raw text cannot be drawn.&lt;br /&gt;
*Spacers do not add a space/newline, giving a &amp;quot;cramped&amp;quot; feel.&lt;br /&gt;
*Users must press &amp;quot;ESC&amp;quot; or be at their console to view the menu.&lt;br /&gt;
&lt;br /&gt;
Radio Style drawing rules/limitations:&lt;br /&gt;
*Max items per page is 10.&lt;br /&gt;
*Titles appear white; items appear yellow, unless disabled, in which case they are white.&lt;br /&gt;
*The 0th item is always white.  For consistency, this means navigational controls explained in the next section are always white, and simply not drawn if disabled.&lt;br /&gt;
&lt;br /&gt;
==Menus==&lt;br /&gt;
Lastly, there are plain ''Menus'' (&amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; in C++).  These are helper objects designed for storing a menu based on selectable items.  Unlike low-level panels, menus are containers for '''items''', and can only contain items which are selectable (i.e., do not contain raw text).  They fall into two categories:&lt;br /&gt;
*Non-paginated: The menu can only have a certain number of items on it, and no control/navigation options will be added, except for an &amp;quot;Exit&amp;quot; button which will always be in the last position supported by the style.&lt;br /&gt;
**Valve Style maximum items: 8&lt;br /&gt;
**Radio Style maximum items: 10&lt;br /&gt;
*Paginated: The menu can have any number of items.  When displayed, only a certain number of items will be drawn at a time.  Automatic navigation controls are added so players can easily move back and forth to different &amp;quot;pages&amp;quot; of items in the menu.&lt;br /&gt;
**&amp;quot;Previous&amp;quot; is always drawn as the first navigation item, third from the last supported position.  This will not be drawn if the menu only contains one page.  If there are no previous pages, the text will not be drawn on either style; if possible, the menu will be padded so spacing is consistent.&lt;br /&gt;
***Valve Style position: 6&lt;br /&gt;
***Radio Style position: 8&lt;br /&gt;
**&amp;quot;Next&amp;quot; is always drawn as the second navigation item, second from the last supported position.  This will not be drawn if the menu only contains one page.  If there are no further pages, the text will not be drawn on either style; if possible, the menu will be padded so spacing is consistent.&lt;br /&gt;
***Valve Style position: 7&lt;br /&gt;
***Radio Style position: 9&lt;br /&gt;
**&amp;quot;Exit&amp;quot; is drawn if the menu has the exit button property set.  It is always the last supported item position.&lt;br /&gt;
***Valve Style position: 8&lt;br /&gt;
***Radio Style position: 10&lt;br /&gt;
&lt;br /&gt;
The purpose of Menus is to simplify the procedure of storing, drawing, and calculating the selection of items.  Thus, menus do not allow for adding raw text, as that would considerably complicate the drawing algorithm.  ''Note: The C++ API supports hooking &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; drawing procedures and adding raw text; this will be added to the scripting API soon.''&lt;br /&gt;
&lt;br /&gt;
Internally, Menus are drawn via a ''RenderMenu'' algorithm.  This algorithm creates a temporary panel and fills it with items from menus.  This panel is then displayed to a client.  The algorithm attempts to create a consistent feel across all menus, and across all styles.  Thus any menu displayed via the &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; class, or &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; Handles, will look and act the same, and the Menu API is based off the Panel API.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Callbacks=&lt;br /&gt;
==Overview==&lt;br /&gt;
Menus are a callback based system.  Each callback represents an action that occurs during a ''menu display cycle''.  A cycle consists of a number of notifications:&lt;br /&gt;
*Start notification.&lt;br /&gt;
**Display notification if the menu can be displayed to the client.&lt;br /&gt;
**Either an item select or menu cancel notification.&lt;br /&gt;
*End notification.&lt;br /&gt;
&lt;br /&gt;
Since ''End'' signifies the end of a full display cycle, it is usually used to destroy temporary menus.&lt;br /&gt;
&lt;br /&gt;
==Specification==&lt;br /&gt;
A detailed explanation of these events is below.  For C++, an &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; pointer is always available.  For SourcePawn, a &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; Handle and a &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; are always set in the &amp;lt;tt&amp;gt;MenuHandler&amp;lt;/tt&amp;gt; callback.  Unlike C++, the SourcePawn API allows certain actions to only be called if they are requested at menu creation time.  This is an optimization.  However, certain actions cannot be prevented from being called.&lt;br /&gt;
&lt;br /&gt;
*'''Start'''.  The menu has been acknowledged.  This does not mean it will be displayed; however, it guarantees that &amp;quot;OnMenuEnd&amp;quot; will be called.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuStart()&amp;lt;/tt&amp;gt; in C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; in SourcePawn.  This action is not triggered unless requested.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Ignored (always 0).&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Ignored (always 0).&lt;br /&gt;
*'''Display'''.  The menu is being displayed to a client.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuDisplay()&amp;lt;/tt&amp;gt; in C++.  An &amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; pointer and client index are available.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Display&amp;lt;/tt&amp;gt; in SourcePawn.  This action is not triggered unless requested.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: A client index.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: A Handle to a menu panel.&lt;br /&gt;
*'''Select'''.  An item on the menu has been selected.  The item position given will be the position in the menu, rather than the key pressed (unless the menu is a raw panel).  &lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; in C++.  A client index and item position are passed.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; in SourcePawn.  This action is always triggerable, whether requested or not.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: A client index.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: An item position.&lt;br /&gt;
*'''Cancel'''.  The menu's display to one client has been cancelled.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; in C++.  A reason for cancellation is provided.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; in SourcePawn.  This action is always triggerable, whether requested or not.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: A client index.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: A menu cancellation reason code.&lt;br /&gt;
*'''End'''.  The menu's display cycle has finished; this means that the &amp;quot;Start&amp;quot; action has occurred, and either &amp;quot;Select&amp;quot; or &amp;quot;Cancel&amp;quot; has occurred thereafter.  This is typically where menu resources are removed/deleted.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuEnd()&amp;lt;/tt&amp;gt; in C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; in SourcePawn.  This action is always triggered, whether requested or not.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: A menu end reason code.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: If param1 was MenuEnd_Cancelled, this contains a menu cancellation reason code.&lt;br /&gt;
&lt;br /&gt;
==Panels==&lt;br /&gt;
For panels, the callback rules change.  Panels only receive two of the above callbacks, and it is guaranteed that only one of them will be called for a given display cycle.  For C++, the &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; pointer will always be &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;.  For SourcePawn, the menu Handle will always be &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*'''Select'''.  A key has been pressed.  This can be any number and should not be considered as reliably in bounds.  For example, even if you only had 2 items in your panel, a client could trigger a key press of &amp;quot;43.&amp;quot;&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; in C++.  A client index and key number pressed are passed.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; in SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: A client index.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Number of the key pressed.&lt;br /&gt;
*'''Cancel'''.  The menu's display to one client has been cancelled.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; in C++.  A reason for cancellation is provided.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; in SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: A client index.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: A menu cancellation reason code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Examples=&lt;br /&gt;
First, let's start off with a very basic menu.  We want the menu to look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Do you like apples?&lt;br /&gt;
1. Yes&lt;br /&gt;
2. No&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We'll draw this menu with both a basic Menu and a Panel to show the API differences.&lt;br /&gt;
&lt;br /&gt;
==Basic Menu==&lt;br /&gt;
First, let's write our example using the Menu building API.  For a more in-depth guide, see [[Menus Step By Step (SourceMod Scripting)]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sourcepawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
    RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
    /* If an option was selected, tell the client about the item. */&lt;br /&gt;
    if (action == MenuAction_Select)&lt;br /&gt;
    {&lt;br /&gt;
        char info[32];&lt;br /&gt;
        bool found = menu.GetItem(param2, info, sizeof(info));&lt;br /&gt;
        PrintToConsole(param1, &amp;quot;You selected item: %d (found? %d info: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
    }&lt;br /&gt;
    /* If the menu was cancelled, print a message to the server about it. */&lt;br /&gt;
    else if (action == MenuAction_Cancel)&lt;br /&gt;
    {&lt;br /&gt;
        PrintToServer(&amp;quot;Client %d's menu was cancelled.  Reason: %d&amp;quot;, param1, param2);&lt;br /&gt;
    }&lt;br /&gt;
    /* If the menu has ended, destroy it */&lt;br /&gt;
    else if (action == MenuAction_End)&lt;br /&gt;
    {&lt;br /&gt;
        delete menu;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
    Menu menu = new Menu(MenuHandler1);&lt;br /&gt;
    menu.SetTitle(&amp;quot;Do you like apples?&amp;quot;);&lt;br /&gt;
    menu.AddItem(&amp;quot;yes&amp;quot;, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
    menu.AddItem(&amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
    menu.ExitButton = false;&lt;br /&gt;
    menu.Display(client, 20);&lt;br /&gt;
&lt;br /&gt;
    return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/sourcepawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note a few very important points from this example:&lt;br /&gt;
*One of either &amp;lt;tt&amp;gt;Select&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;Cancel&amp;lt;/tt&amp;gt; will always be sent to the action handler.&lt;br /&gt;
*&amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; will always be sent to the action handler.&lt;br /&gt;
*We destroy our Menu in the &amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; action, because our Handle is no longer needed.  If we had destroyed the Menu after &amp;lt;tt&amp;gt;DisplayMenu&amp;lt;/tt&amp;gt;, it would have canceled the menu's display to the client.&lt;br /&gt;
*Menus, by default, have an exit button.  We disabled this in our example.&lt;br /&gt;
*Our menu is set to display for 20 seconds.  That means that if the client does not select an item within 20 seconds, the menu will be canceled.  This is usually desired for menus that are for voting.  Note that unlike AMX Mod X, you do not need to set a timer to make sure the menu will be ended.&lt;br /&gt;
*Although we created and destroyed a new Menu Handle, we didn't need to.  It is perfectly acceptable to create the Handle once for the lifetime of the plugin.&lt;br /&gt;
&lt;br /&gt;
Our finished menu and attached console output looks like this (I selected &amp;quot;Yes&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Basic Panel==&lt;br /&gt;
Now, let's rewrite our example to use Panels instead.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sourcepawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
    RegConsoleCmd(&amp;quot;panel_test1&amp;quot;, Panel_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int PanelHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
    if (action == MenuAction_Select)&lt;br /&gt;
    {&lt;br /&gt;
        PrintToConsole(param1, &amp;quot;You selected item: %d&amp;quot;, param2);&lt;br /&gt;
    }&lt;br /&gt;
    else if (action == MenuAction_Cancel)&lt;br /&gt;
    {&lt;br /&gt;
        PrintToServer(&amp;quot;Client %d's menu was cancelled.  Reason: %d&amp;quot;, param1, param2);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Panel_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
    Panel panel = new Panel();&lt;br /&gt;
    panel.SetTitle(&amp;quot;Do you like apples?&amp;quot;);&lt;br /&gt;
    panel.DrawItem(&amp;quot;Yes&amp;quot;);&lt;br /&gt;
    panel.DrawItem(&amp;quot;No&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    panel.Send(client, PanelHandler1, 20);&lt;br /&gt;
&lt;br /&gt;
    delete panel;&lt;br /&gt;
&lt;br /&gt;
    return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/sourcepawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, Panels are significantly different.&lt;br /&gt;
*We can destroy the Panel as soon as we're done displaying it.  We can create the Panel once and keep re-using it, but we can destroy it at any time without interrupting client menus.&lt;br /&gt;
*The Handler function gets much less data.  Since panels are designed as a raw display, no &amp;quot;item&amp;quot; information is saved internally.  Thus, the handler function only knows whether the display was canceled or whether (and what) numerical key was pressed.&lt;br /&gt;
*There is no automation.  You cannot add more than a certain amount of selectable items to a Panel and get pagination.  Automated control functionality requires using the heftier Menu object API.&lt;br /&gt;
&lt;br /&gt;
Our finished display and console output looks like this (I selected &amp;quot;Yes&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_panel_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Basic Paginated Menu==&lt;br /&gt;
Now, let's take a more advanced example -- pagination. Let's say we want to build a menu for changing the map.  An easy way to do this is to read the &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; file at the start of a plugin and build a menu out of it.&lt;br /&gt;
&lt;br /&gt;
Since reading and parsing a file is an expensive operation, we only want to do this once per map.  Thus we'll build the menu in &amp;lt;tt&amp;gt;OnMapStart&amp;lt;/tt&amp;gt;, and we won't call &amp;lt;tt&amp;gt;CloseHandle&amp;lt;/tt&amp;gt; until &amp;lt;tt&amp;gt;OnMapEnd&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Source code:&lt;br /&gt;
&amp;lt;sourcepawn&amp;gt;Menu g_MapMenu = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
    RegConsoleCmd(&amp;quot;menu_changemap&amp;quot;, Command_ChangeMap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
    g_MapMenu = BuildMapMenu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnMapEnd()&lt;br /&gt;
{&lt;br /&gt;
    delete g_MapMenu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Menu BuildMapMenu()&lt;br /&gt;
{&lt;br /&gt;
    /* Open the file */&lt;br /&gt;
    File file = OpenFile(&amp;quot;maplist.txt&amp;quot;, &amp;quot;rt&amp;quot;);&lt;br /&gt;
    if (file == null)&lt;br /&gt;
    {&lt;br /&gt;
        return null;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Create the menu Handle */&lt;br /&gt;
    Menu menu = new Menu(Menu_ChangeMap);&lt;br /&gt;
    char mapname[255];&lt;br /&gt;
    while (!file.EndOfFile() &amp;amp;&amp;amp; file.ReadLine(mapname, sizeof(mapname)))&lt;br /&gt;
    {&lt;br /&gt;
        if (mapname[0] == ';' || !IsCharAlpha(mapname[0]))&lt;br /&gt;
        {&lt;br /&gt;
            continue;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Cut off the name at any whitespace */&lt;br /&gt;
        int len = strlen(mapname);&lt;br /&gt;
        for (int i = 0; i &amp;lt; len; i++)&lt;br /&gt;
        {&lt;br /&gt;
            if (IsCharSpace(mapname[i]))&lt;br /&gt;
            {&lt;br /&gt;
                mapname[i] = '\0';&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Check if the map is valid */&lt;br /&gt;
        if (!IsMapValid(mapname))&lt;br /&gt;
        {&lt;br /&gt;
            continue;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /* Add it to the menu */&lt;br /&gt;
        menu.AddItem(mapname, mapname);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* Make sure we close the file! */&lt;br /&gt;
    file.Close();&lt;br /&gt;
&lt;br /&gt;
    /* Finally, set the title */&lt;br /&gt;
    menu.SetTitle(&amp;quot;Please select a map:&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    return menu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public int Menu_ChangeMap(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
    if (action == MenuAction_Select)&lt;br /&gt;
    {&lt;br /&gt;
        char info[32];&lt;br /&gt;
&lt;br /&gt;
        /* Get item info */&lt;br /&gt;
        bool found = menu.GetItem(param2, info, sizeof(info));&lt;br /&gt;
&lt;br /&gt;
        /* Tell the client */&lt;br /&gt;
        PrintToConsole(param1, &amp;quot;You selected item: %d (found? %d info: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
&lt;br /&gt;
        /* Change the map */&lt;br /&gt;
        ServerCommand(&amp;quot;changelevel %s&amp;quot;, info);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_ChangeMap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
    if (g_MapMenu == null)&lt;br /&gt;
    {&lt;br /&gt;
        PrintToConsole(client, &amp;quot;The maplist.txt file was not found!&amp;quot;);&lt;br /&gt;
        return Plugin_Handled;&lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    g_MapMenu.Display(client, MENU_TIME_FOREVER);&lt;br /&gt;
&lt;br /&gt;
    return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/sourcepawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This menu results in many selections (my &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; file had around 18 maps).  So, our final menu has 3 pages, which side by side, look like:&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_2_page1.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page2.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page3.PNG]]&lt;br /&gt;
&lt;br /&gt;
Finally, the console output printed this before the map changed to my selection, &amp;lt;tt&amp;gt;cs_office&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;You selected item: 8 (found? 1 info: cs_office)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Displaying and designing this Menu with a raw &amp;lt;tt&amp;gt;ShowMenu&amp;lt;/tt&amp;gt; message or &amp;lt;tt&amp;gt;Panel&amp;lt;/tt&amp;gt; API would be very time consuming and difficult.  We would have to keep track of all the items in an array of hardcoded size, pages which the user is viewing, and write a function which calculated item selection based on current page and key press.  The Menu system, thankfully, handles all of this for you.&lt;br /&gt;
&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
*Control options which are not available are not drawn.  For example, in the first page, you cannot go &amp;quot;back,&amp;quot; and in the last page, you cannot go &amp;quot;next.&amp;quot;  Despite this, the menu API tries to keep each the interface as consistent as possible.  Thus, visually, each navigational control is always in the same position.  &lt;br /&gt;
*Although we specified no time out for our menu, if we had placed a timeout, flipping through pages does not affect the overall time.  For example, if we had a timeout of 20, each successive page flip would continue to detract from the overall display time, rather than restart the allowed hold time back to 20.&lt;br /&gt;
*If we had disabled the Exit button, options 8 and 9 would still be &amp;quot;Back&amp;quot; and &amp;quot;Next,&amp;quot; respectively.&lt;br /&gt;
*Again, we did not free the Menu Handle in &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;.  This is because our menu is global/static, and we don't want to rebuild it every time.&lt;br /&gt;
*These images show &amp;quot;Back.&amp;quot;  In SourceMod revisions 1011 and higher, &amp;quot;Back&amp;quot; is changed to &amp;quot;Previous,&amp;quot; and &amp;quot;Back&amp;quot; is reserved for the special &amp;quot;ExitBack&amp;quot; functionality.&lt;br /&gt;
&lt;br /&gt;
=Voting=&lt;br /&gt;
SourceMod also has API for displaying menus as votable choices to more than one client.  SourceMod automatically handles selecting an item and randomly picking a tie-breaker.  The voting API adds two new &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; values, which for vote displays, are '''always''' passed:&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteStart&amp;lt;/tt&amp;gt;: Fired after &amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; when the voting has officially started.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;: Fired when all clients have either voted or cancelled their vote menu.  The chosen item is passed through &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;.  This is fired '''before''' &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;.  It is important to note that it does not supercede &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, nor is it the same thing.  Menus should never be destroyed in &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;.  '''Note:''' This is not called if &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;() is used.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteCancel&amp;lt;/tt&amp;gt;: Fired if the menu is cancelled while the vote is in progress.  If this is called, &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; or the result callback will not be called, but &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; will be afterwards.  A vote cancellation reason is passed in &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
The voting system extends overall menus with two additional properties:&lt;br /&gt;
*Only one vote can be active at a time.  You must call &amp;lt;tt&amp;gt;IsVoteInProgress&amp;lt;/tt&amp;gt;() or else &amp;lt;tt&amp;gt;VoteMenu&amp;lt;/tt&amp;gt;() will fail.&lt;br /&gt;
*If a client votes and then disconnects while the vote is still active, the client's vote will be invalidated.&lt;br /&gt;
&lt;br /&gt;
The example below shows has to create a function called &amp;lt;tt&amp;gt;DoVoteMenu()&amp;lt;/tt&amp;gt; which will ask all clients whether or not they would like to change to the given map.&lt;br /&gt;
&lt;br /&gt;
==Simple Vote==&lt;br /&gt;
&amp;lt;sourcepawn&amp;gt;public int Handle_VoteMenu(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
    if (action == MenuAction_End)&lt;br /&gt;
    {&lt;br /&gt;
        /* This is called after VoteEnd */&lt;br /&gt;
        delete menu;&lt;br /&gt;
    }&lt;br /&gt;
    else if (action == MenuAction_VoteEnd)&lt;br /&gt;
    {&lt;br /&gt;
        /* 0=yes, 1=no */&lt;br /&gt;
        if (param1 == 0)&lt;br /&gt;
        {&lt;br /&gt;
            char map[64];&lt;br /&gt;
            menu.GetItem(param1, map, sizeof(map));&lt;br /&gt;
            ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void DoVoteMenu(const char[] map)&lt;br /&gt;
{&lt;br /&gt;
    if (IsVoteInProgress())&lt;br /&gt;
    {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    Menu menu = new Menu(Handle_VoteMenu);&lt;br /&gt;
    menu.SetTitle(&amp;quot;Change map to: %s?&amp;quot;, map);&lt;br /&gt;
    menu.AddItem(map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
    menu.AddItem(&amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
    menu.ExitButton = false;&lt;br /&gt;
    menu.DisplayVoteToAll(20);&lt;br /&gt;
}&amp;lt;/sourcepawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Advanced Voting==&lt;br /&gt;
If you need more information about voting results than &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; gives you, you can choose to have a different callback invoked.  The new callback will provide much more information, but at a price: &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; will not be called, and you will have to decide how to interpret the results.  This is done via &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;sourcepawn&amp;gt;public int Handle_VoteMenu(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
    if (action == MenuAction_End)&lt;br /&gt;
    {&lt;br /&gt;
        /* This is called after VoteEnd */&lt;br /&gt;
        delete menu;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Handle_VoteResults(Menu menu, &lt;br /&gt;
        int num_votes, &lt;br /&gt;
        int num_clients, &lt;br /&gt;
        const int[][] client_info, &lt;br /&gt;
        int num_items, &lt;br /&gt;
        const int[][] item_info)&lt;br /&gt;
{&lt;br /&gt;
    /* See if there were multiple winners */&lt;br /&gt;
    int winner = 0;&lt;br /&gt;
    if (num_items &amp;gt; 1&lt;br /&gt;
    &amp;amp;&amp;amp; (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))&lt;br /&gt;
    {&lt;br /&gt;
        winner = GetRandomInt(0, 1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char map[64];&lt;br /&gt;
    menu.GetItem(item_info[winner][VOTEINFO_ITEM_INDEX], map, sizeof(map));&lt;br /&gt;
    ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void DoVoteMenu(const char[] map)&lt;br /&gt;
{&lt;br /&gt;
    if (IsVoteInProgress())&lt;br /&gt;
    {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    Menu menu = new Menu(Handle_VoteMenu);&lt;br /&gt;
    menu.VoteResultCallback = Handle_VoteResults;&lt;br /&gt;
    menu.SetTitle(&amp;quot;Change map to: %s?&amp;quot;, map);&lt;br /&gt;
    menu.AddItem(map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
    menu.AddItem(&amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
    menu.ExitButton = false;&lt;br /&gt;
    menu.DisplayVoteToAll(20);&lt;br /&gt;
}&amp;lt;/sourcepawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=ExitBack=&lt;br /&gt;
ExitBack is a special term to refer to the &amp;quot;ExitBack Button.&amp;quot;  This button is disabled by default.  Normally, paginated menus have no &amp;quot;Previous&amp;quot; item for the first page.  If the &amp;quot;ExitBack&amp;quot; button is enabled, the &amp;quot;Previous&amp;quot; item will show up as &amp;quot;Back.&amp;quot;  &lt;br /&gt;
&lt;br /&gt;
Selecting the &amp;quot;ExitBack&amp;quot; option will exit the menu with &amp;lt;tt&amp;gt;MenuCancel_ExitBack&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;MenuEnd_ExitBack&amp;lt;/tt&amp;gt;.  The functionality of this is the same as a normal menu exit internally; extra functionality must be defined through the callbacks.&lt;br /&gt;
&lt;br /&gt;
=Closing Menu Handles=&lt;br /&gt;
It is only necessary to close a menu handle on &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;.  The &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; is done every time a menu is closed and no longer needed.&lt;br /&gt;
&lt;br /&gt;
=Translations=&lt;br /&gt;
It is possible to dynamically translate menus to each player through the &amp;lt;tt&amp;gt;MenuAction_DisplayItem&amp;lt;/tt&amp;gt; callback.  A special native, &amp;lt;tt&amp;gt;RedrawMenuItem&amp;lt;/tt&amp;gt;, is used to transform the text while inside the callback.  Let's redo the vote example from earlier to be translated:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sourcepawn&amp;gt;public int Handle_VoteMenu(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
    if (action == MenuAction_End)&lt;br /&gt;
    {&lt;br /&gt;
        /* This is called after VoteEnd */&lt;br /&gt;
        delete menu;&lt;br /&gt;
    }&lt;br /&gt;
    else if (action == MenuAction_VoteEnd)&lt;br /&gt;
    {&lt;br /&gt;
        /* 0=yes, 1=no */&lt;br /&gt;
        if (param1 == 0)&lt;br /&gt;
        {&lt;br /&gt;
            char map[64];&lt;br /&gt;
            menu.GetItem(param1, map, sizeof(map));&lt;br /&gt;
            ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    else if (action == MenuAction_DisplayItem)&lt;br /&gt;
    {&lt;br /&gt;
        /* Get the display string, we'll use it as a translation phrase */&lt;br /&gt;
        char display[64];&lt;br /&gt;
        menu.GetItem(param2, &amp;quot;&amp;quot;, 0, _, display, sizeof(display));&lt;br /&gt;
&lt;br /&gt;
        /* Translate the string to the client's language */&lt;br /&gt;
        char buffer[255];&lt;br /&gt;
        Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, display, param1);&lt;br /&gt;
&lt;br /&gt;
        /* Override the text */&lt;br /&gt;
        return RedrawMenuItem(buffer);&lt;br /&gt;
    }&lt;br /&gt;
    else if (action == MenuAction_Display)&lt;br /&gt;
    {&lt;br /&gt;
        /* Panel Handle is the second parameter */&lt;br /&gt;
        Panel panel = view_as&amp;lt;Panel&amp;gt;(param2);&lt;br /&gt;
&lt;br /&gt;
        /* Get the map name we're changing to from the first item */&lt;br /&gt;
        char map[64];&lt;br /&gt;
        menu.GetItem(0, map, sizeof(map));&lt;br /&gt;
&lt;br /&gt;
        /* Translate to our phrase */&lt;br /&gt;
        char buffer[255];&lt;br /&gt;
        Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Change map to?&amp;quot;, client, map);&lt;br /&gt;
&lt;br /&gt;
        panel.SetTitle(buffer);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void DoVoteMenu(const char[] map)&lt;br /&gt;
{&lt;br /&gt;
    if (IsVoteInProgress())&lt;br /&gt;
    {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    Menu menu = new Menu(Handle_VoteMenu,MenuAction_DisplayItem|MenuAction_Display);&lt;br /&gt;
    menu.SetTitle(&amp;quot;Change map to: %s?&amp;quot;, map);&lt;br /&gt;
    menu.AddItem(map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
    menu.AddItem(&amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
    menu.ExitButton = false;&lt;br /&gt;
    menu.DisplayVoteToAll(20);&lt;br /&gt;
}&amp;lt;/sourcepawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11204</id>
		<title>Menu API (SourceMod)=ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Menu_API_(SourceMod)%3Dru&amp;diff=11204"/>
		<updated>2021-07-20T13:30:44Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Kleiner moved page Ru:Menu API (SourceMod) to Menu API (SourceMod)/ru: Language change support&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;SourceMod обладает большим API для создания и отображений меню для клиентов. В отличии от AMX Mod X, API SourceMod'a обладает широким функционалом. Меню базируются на обратных вызовах (callbacks), которые гарантированно будут вызваны.&lt;br /&gt;
&lt;br /&gt;
Для C++, API меню может быть найдено в &amp;lt;tt&amp;gt;public/IMenuManager.h&amp;lt;/tt&amp;gt;. Для SourcePawn - &amp;lt;tt&amp;gt;scripting/include/menus.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Объекты=&lt;br /&gt;
Система меню SourceMod основана на объектно ориентированной иерархии. Понимание этой иерархии, даже для скриптинга, является ключевым моментом для эффективного использования меню.&lt;br /&gt;
&lt;br /&gt;
==Стили==&lt;br /&gt;
Высший уровень объектов называется ''MenuStyle'' (&amp;lt;tt&amp;gt;IMenuStyle&amp;lt;/tt&amp;gt; в C++). Стили описывают уникальную систему меню. Всего есть два стиля, встроенных в SourceMod:&lt;br /&gt;
*Valve Styly, так же называемым &amp;quot;ESC&amp;quot; меню; 8 пунктов на странице, нельзя добавить текст без занятия слота или спрятать (отключить) текст.&lt;br /&gt;
*Radio Style, так же называемый &amp;quot;AMX&amp;quot; меню; 10 пунктов на странице, можно добавить любой текст.&lt;br /&gt;
&lt;br /&gt;
Каждый MenuStyle обладает своими правилами и свойствами. Вы можете считать, что каждый из них существует на раздельных &amp;quot;каналах&amp;quot;. Например, два разных меню могут быть на экране игрока, будучи Valve и Radio меню одновременно, и SourceMod сможет обработать их обоих без всяких проблем. Это всё возможно благодаря тому, что каждый стиль отслеживает свои меню отдельно.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Меню низшего уровня называется ''Панелью'' (&amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; в C++). Панель - один ''''кусок'''' текста. Пункты и текст могут добавляться к панели столько раз, сколько поддерживает родительский стиль. Например, Valve стиль не поддерживает отображение текста без занятия слота или отключенных пунктов. Однако с Radio стилем панели это становится возможным, и вы можете отобразить большое количество текста так, как вам это захочется. &lt;br /&gt;
&lt;br /&gt;
Панель считается временным объектом. Она создается, рендерится, отображается, и затем удаляется. Хотя она может быть сохранена на неопределенное время, если в этом есть необходимость.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Vavle стиля:&lt;br /&gt;
*Максимум пунктов на странице: 8&lt;br /&gt;
*Отключенный текст не отображается.&lt;br /&gt;
*Нельзя написать текст, не заняв слота.&lt;br /&gt;
*Пробелы не добавляются к пробелам\новым линиям, добавляя чувство &amp;quot;ограниченности&amp;quot;. &lt;br /&gt;
*Пользователь должен нажать &amp;quot;ESC&amp;quot; или быть в своей консоли, чтобы увидеть меню.&lt;br /&gt;
&lt;br /&gt;
Правила и ограничения Radio стиля:&lt;br /&gt;
*10 пунктов на странице&lt;br /&gt;
*Заглавия белые; пункты желтые, если не отключены. Если отключены, то белого цвета.&lt;br /&gt;
*Нулевой элемент всегда белый. Для согласованности это значит, что навигационное управление всегда белое и находится в следующей секции, и просто не отрисовывается, если отсутствует.&lt;br /&gt;
&lt;br /&gt;
==Меню==&lt;br /&gt;
Наконец, есть просто ''Меню'' (&amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; in C++). Это вспомогательный объект, созданный для хранения базированных на меню выбираемых пунктов. В отличии от низкоуровневых панелей, меню содержит '''пункты''', и может содержать только те пункты, которые можно выбрать (т.е. занимающие слоты). Они делятся на две категории:&lt;br /&gt;
*Не нумерованные: эти меню могут иметь определенное число пунктов, и не имеют навигационных опций, кроме кнопки &amp;quot;Выход&amp;quot;, которая всегда добавляется на последнюю позицию, поддерживаемую стилем.&lt;br /&gt;
**Valve Стиль: максимум пунктов: 8&lt;br /&gt;
**Radio Стиль: максимум пунктов: 10&lt;br /&gt;
*Нумерованные: такие меню могут содержать сколь угодно пунктов. Во время отображения будет показано только определенное число пунктов. Автоматически будут добавлены навигационные пункты, таким образом, игроки легко могут возвращаться назад и вперед к определенным &amp;quot;страницам&amp;quot; пунктов меню.&lt;br /&gt;
**&amp;quot;Назад&amp;quot; всегда отображается как первый навигационный пункт, третий с конца поддерживаемых позиций. Не будет добавлен, если меню содержит только одну страницу. Если нет никаких предыдущих страниц, текст никогда не будет отображен, ни в каком стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 6&lt;br /&gt;
***Radio Стиль, позиция: 8&lt;br /&gt;
**&amp;quot;Вперед&amp;quot; всегда отображается как второй навигационный пункт, второй с конца из доступных позиций. Не отображается, если меню состоит из одной страницы. Если нет следующих страниц, то текс не будет отображен при любом стиле. Если возможно, то меню будет дополнено необходимыми пробелами.&lt;br /&gt;
***Valve Стиль, позиция: 7&lt;br /&gt;
***Radio Стиль, позиция: 9&lt;br /&gt;
**&amp;quot;Выход&amp;quot; отображается, если у меню включена кнопка &amp;quot;выход&amp;quot;. Всегда занимает последнюю из возможных позиций&lt;br /&gt;
***Valve Стиль, позиция: 8&lt;br /&gt;
***Radio Стиль, позиция: 10&lt;br /&gt;
&lt;br /&gt;
Цель меню - упростить процедуру хранения, отображения, и вычисления выбранного пункта. Таким образом, меню не позволяет добавлять пустой текст, так как это значительно усложняет алгоритм отображения.  &lt;br /&gt;
&lt;br /&gt;
Внутренне, меню отображается через ''RenderMenu'' алогритм. Этот алгоритм создает временную панель и заполняет её пунктами из меню. Эта панель отображается клиенту. Алгоритм пытается создать свободное перемещение по всем меню, и по всем стилям. Таким образом, все меню отображаются через &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; класс, или &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; дескрипторы (Handles), и будет выглядеть и действовать так же, как и меню API, основанное на панельном API.&lt;br /&gt;
&lt;br /&gt;
=Обратные вызовы (callbacks)=&lt;br /&gt;
==Краткий обзор==&lt;br /&gt;
Меню базируются на системе обратных вызовов. Каждый обратный вызов предоставляет собой действие, которые происходят во время ''цикла отображения меню''. Цикл состоит из множества уведомлений:&lt;br /&gt;
*Начало уведомления.&lt;br /&gt;
**Уведомление об отображении, если меню может быть отображено клиенту.&lt;br /&gt;
**Уведомление о выборе пункта или кнопки отмены.&lt;br /&gt;
*Конец уведомления.&lt;br /&gt;
&lt;br /&gt;
Так как ''End'' обозначает конец полного цикла отображения, то обычно его используют для уничтожения временных меню.&lt;br /&gt;
&lt;br /&gt;
==Спецификация==&lt;br /&gt;
Детальное объяснение этих событий ниже. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель всегда доступен. Для SourcePawn, &amp;lt;tt&amp;gt;Menu&amp;lt;/tt&amp;gt; хандл и &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt; всегда установлены в &amp;lt;tt&amp;gt;MenuHandler&amp;lt;/tt&amp;gt; callback. В отличии от C++, SourcePawn API позволяет вызвать лишь некоторые действия, если они были запрошены во время создания меню. Это сделано в целях оптимизации. Однако, эти действия не могут быть не вызваны.&lt;br /&gt;
&lt;br /&gt;
*'''Start'''. Меню было признано. Это не означает, что меню будет отображено; однако, это гарантирует, что событие &amp;quot;OnMenuEnd&amp;quot; будет вызвано.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuStart()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Игнорируется (всегда 0).&lt;br /&gt;
*'''Display'''.  Меню отобразилось клиенту.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuDisplay()&amp;lt;/tt&amp;gt; в C++. &amp;lt;tt&amp;gt;IMenuPanel&amp;lt;/tt&amp;gt; доступны указатель и индекс клиента.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Display&amp;lt;/tt&amp;gt; в SourcePawn. Не будет вызвано, если не было запрошено.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Хандл меню.&lt;br /&gt;
*'''Select'''. Пункт был выбран. Будет дана позиция пункта в меню, а не нажатая кнопка (если меню не является панелью) &lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и позиция пункта.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Позиция пункта.&lt;br /&gt;
*'''Cancel'''.  Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
*'''End'''. Цикл отображения меню закончился; это означает, что &amp;quot;Start&amp;quot; действие было, и одно из действий &amp;quot;Select&amp;quot; или &amp;quot;Cancel&amp;quot; так же произошло. Обычно в этом месте отчищаются ресурсы меню или само меню удаляется.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuEnd()&amp;lt;/tt&amp;gt; в C++.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; в SourcePawn. Это действие всегда вызывается, вне зависимости от запроса.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Причина завершения меню.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: если param1 == MenuEnd_Cancelled, то здесь содержится код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
==Панели==&lt;br /&gt;
Для панелей цикл жизни другой. Панели могут вызывать лишь два callback'а из написанных выше, и гарантируется, что один из них точно будет вызван для данного цикла отображения. Для C++, &amp;lt;tt&amp;gt;IBaseMenu&amp;lt;/tt&amp;gt; указатель будет всегда &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt;. Для SourcePawn, меню Handle будет всегда &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*'''Select'''.  Кнопка была нажата. Этой кнопкой может быть любое число. Например, если в панели у вас 2 пункта, то клиент может вызвать это событие, нажав &amp;quot;43&amp;quot;.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuSelect()&amp;lt;/tt&amp;gt; в C++. Передаются индекс клиента и нажатая кнопка.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Select&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Номер нажатой клавиши.&lt;br /&gt;
*'''Cancel'''. Отображение меню у одного из клиентов было закрыто.&lt;br /&gt;
**&amp;lt;tt&amp;gt;OnMenuCancel()&amp;lt;/tt&amp;gt; в C++. Доступна причина закрытия.&lt;br /&gt;
**&amp;lt;tt&amp;gt;MenuAction_Cancel&amp;lt;/tt&amp;gt; в SourcePawn.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;: Индекс клиента.&lt;br /&gt;
***&amp;lt;tt&amp;gt;param2&amp;lt;/tt&amp;gt;: Код причины закрытия меню.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Примеры=&lt;br /&gt;
Для начала попробуем сделать очень простое меню:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы любите яблоки?&lt;br /&gt;
1. Да&lt;br /&gt;
2. Нет&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Сделаем это как меню и как панель, чтобы увидеть различия в их API.&lt;br /&gt;
&lt;br /&gt;
==Простое меню==&lt;br /&gt;
Сперва напишем наш пример через API Меню. Для пошагового руководства смотрите [[Menus Step By Step (SourceMod Scripting)]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду menu_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_test1&amp;quot;, Menu_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// хандл меню&lt;br /&gt;
int MenuHandler1(Menu menu, MenuAction action, int param1, int param2)&lt;br /&gt;
{&lt;br /&gt;
	/* Если был выбран какой-либо пункт, то сообщим клиенту о его выборе. */&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		char info[32]; // переменная для хранения выбора&lt;br /&gt;
		bool found = GetMenuItem(menu, param2, info, sizeof(info)); // получаем информацию о выбранном в меню пункте&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы нажали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info); // пишем клиенту в консоль&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню было отменено, то сообщим об этом серверу. */&lt;br /&gt;
	else if (action == MenuAction_Cancel)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл меню.  Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
	/* Если меню &amp;quot;закончилось&amp;quot;, то удалим его из памяти */&lt;br /&gt;
	else if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		delete menu;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Action Menu_Test1(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:menu = CreateMenu(MenuHandler1);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;да&amp;quot;, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;нет&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	DisplayMenu(menu, client, 20);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несколько очень важных замечаний о этом примере:&lt;br /&gt;
*Одно из &amp;lt;tt&amp;gt;Select&amp;lt;/tt&amp;gt; или &amp;lt;tt&amp;gt;Cancel&amp;lt;/tt&amp;gt; событий всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*&amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; всегда будет отослано обработчику действий (action handler).&lt;br /&gt;
*Мы удаляем наше меню в &amp;lt;tt&amp;gt;End&amp;lt;/tt&amp;gt; действии, потому что наш дескриптор (Handle) нам больше не нужен. Если бы мы удалили его после &amp;lt;tt&amp;gt;DisplayMenu&amp;lt;/tt&amp;gt;, это бы отменило отображение меню клиенту.&lt;br /&gt;
*Меню по умолчанию имеет кнопку выхода. В нашем примере мы его отключили.&lt;br /&gt;
*Наше меню будет отображаться 20 секунд. Это означает, что если клиент ничего не выбрал в течении 20 секунд, то меню будет закрыто. Это обычно необходимо для меню с голосованиями. В отличии от AMX Mod X, вам не нужно создавать таймер, чтобы быть увереным, что меню &amp;quot;закончилось&amp;quot;.&lt;br /&gt;
*Мы создали и уничтожили новый дескриптор меню (Menu Handle), однако мы можем этого и не делать. Вполне допустимо создать дескриптор (Handle) один раз для всего времени жизни плагина.&lt;br /&gt;
*Чтобы нормально отображались русские буквы, кодировка файла должна быть UTF-8 (без BOOM), иначе будут квадратики вместо русских букв.&lt;br /&gt;
&lt;br /&gt;
Наше законченное меню и приложенный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика).&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовая панель==&lt;br /&gt;
Теперь давайте перепишем наш пример с использованием Панели вместо Меню.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	// зарегистрировали консольную команду panel_test1 для открытия меню.&lt;br /&gt;
	RegConsoleCmd(&amp;quot;panel_test1&amp;quot;, Panel_Test1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public PanelHandler1(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d&amp;quot;, param2);&lt;br /&gt;
	} else if (action == MenuAction_Cancel) {&lt;br /&gt;
		PrintToServer(&amp;quot;Клиент %d' закрыл панель. Причина: %d&amp;quot;, param1, param2);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Panel_Test1(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:panel = CreatePanel();&lt;br /&gt;
	SetPanelTitle(panel, &amp;quot;Вы любите яблоки?&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Да&amp;quot;);&lt;br /&gt;
	DrawPanelItem(panel, &amp;quot;Нет&amp;quot;);&lt;br /&gt;
		&lt;br /&gt;
	SendPanelToClient(panel, client, PanelHandler1, 20);&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(panel);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как вы можете заметить, Панели сильно отличаются от Меню:&lt;br /&gt;
*Мы можем удалить Панель как только закончим его отображать. Мы можем создать Панель один раз, и продолжать использовать его много раз, однако мы можем удалить его в любой момент, без нарушения цикла отображения клиентского меню.&lt;br /&gt;
*Обработчик Панели (Handler) получает гораздо меньше информации. Так как Панели разрабатывались как грубый вывод текста, была сохранена возможность добавления информации без занятия слота. Поэтому обработчик может знать лишь об отмене или о нажатии любой клавиши с номером.&lt;br /&gt;
*Отсутствует автоматизация. Вы не можете добавить больше определенного числа выбираемых пунктов и добавить нумерацию. Автоматический контроль требует использования тяжелого объектного API Меню.&lt;br /&gt;
&lt;br /&gt;
Наше законченная панель и приложеный вывод в консоли выглядит как на картинке ниже (был выбран ответ &amp;quot;Да&amp;quot;, и картинка взята с англ вики, прим. переводчика):&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_panel_1.PNG]]&lt;br /&gt;
&lt;br /&gt;
==Базовое меню с нумерацией==&lt;br /&gt;
Теперь сделаем пример с большим функционалом -- нумерацией. Попробуем сделать меню для смены карты. Самый простой путь - прочесть &amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл в начале плагина и создать меню по этому файла.&lt;br /&gt;
&lt;br /&gt;
Так как чтение и парсинг файла довольно долгая операция, мы будем делать это только раз за карту. Поэтому мы построим меню в &amp;lt;tt&amp;gt;OnMapStart&amp;lt;/tt&amp;gt;, и мы не будем вызывать &amp;lt;tt&amp;gt;CloseHandle&amp;lt;/tt&amp;gt; до события &amp;lt;tt&amp;gt;OnMapEnd&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Исходный код:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:g_MapMenu = INVALID_HANDLE&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;menu_changemap&amp;quot;, Command_ChangeMap);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	g_MapMenu = BuildMapMenu();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapEnd()&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu != INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		CloseHandle(g_MapMenu);&lt;br /&gt;
		g_MapMenu = INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Handle:BuildMapMenu()&lt;br /&gt;
{&lt;br /&gt;
	/* открываем файл */&lt;br /&gt;
	new Handle:file = OpenFile(&amp;quot;maplist.txt&amp;quot;, &amp;quot;rt&amp;quot;);&lt;br /&gt;
	if (file == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return INVALID_HANDLE;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	/* создаем хандл меню */&lt;br /&gt;
	new Handle:menu = CreateMenu(Menu_ChangeMap);&lt;br /&gt;
	new String:mapname[255];&lt;br /&gt;
	while (!IsEndOfFile(file) &amp;amp;&amp;amp; ReadFileLine(file, mapname, sizeof(mapname)))&lt;br /&gt;
	{&lt;br /&gt;
		if (mapname[0] == ';' || !IsCharAlpha(mapname[0]))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* выбрасываем все пробелы из названия */&lt;br /&gt;
		new len = strlen(mapname);&lt;br /&gt;
		for (new i=0; i&amp;lt;len; i++)&lt;br /&gt;
		{&lt;br /&gt;
			if (IsCharSpace(mapname[i]))&lt;br /&gt;
			{&lt;br /&gt;
				mapname[i] = '\0';&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		/* проверяем карту на валидность */&lt;br /&gt;
		if (!IsMapValid(mapname))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		/* добавляем карту в меню */&lt;br /&gt;
		AddMenuItem(menu, mapname, mapname);&lt;br /&gt;
	}&lt;br /&gt;
	/* не забываем закрыть файл! */&lt;br /&gt;
	CloseHandle(file);&lt;br /&gt;
	&lt;br /&gt;
	/* наконец, устанавливаем заглавие меню */&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Выберите карту:&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	return menu;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Menu_ChangeMap(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_Select)&lt;br /&gt;
	{&lt;br /&gt;
		new String:info[32];&lt;br /&gt;
&lt;br /&gt;
		/* получаем информацию о пункте */&lt;br /&gt;
		new bool:found = GetMenuItem(menu, param2, info, sizeof(info));&lt;br /&gt;
&lt;br /&gt;
		/* говорим об этом клиенту */&lt;br /&gt;
		PrintToConsole(param1, &amp;quot;Вы выбрали: %d (найдено? %d информация: %s)&amp;quot;, param2, found, info);&lt;br /&gt;
&lt;br /&gt;
		/* меняем карту */&lt;br /&gt;
		ServerCommand(&amp;quot;changelevel %s&amp;quot;, info);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_ChangeMap(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (g_MapMenu == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Файл maplist.txt не был найден!&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}	&lt;br /&gt;
	&lt;br /&gt;
	DisplayMenu(g_MapMenu, client, MENU_TIME_FOREVER);&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результатом такого меню будет множество пунктов (&amp;lt;tt&amp;gt;maplist.txt&amp;lt;/tt&amp;gt; файл содержал примерно 18 карт). Таким образом, конечное меню содержит три страницы, которые идут друг за другом, и выглядят вот так:&lt;br /&gt;
&lt;br /&gt;
[[Image:Basic_menu_2_page1.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page2.PNG]]&lt;br /&gt;
[[Image:Basic_menu_2_page3.PNG]]&lt;br /&gt;
&lt;br /&gt;
Сообщение в консоль будет выведено до смены карты, например, если выбрали &amp;lt;tt&amp;gt;cs_office&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;Вы выбрали: 8 (найдено? 1 информация: cs_office)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отображение и разработка этого меню через &amp;lt;tt&amp;gt;ShowMenu&amp;lt;/tt&amp;gt; сообщение или &amp;lt;tt&amp;gt;Панельный&amp;lt;/tt&amp;gt; API занимает много времени и является довольно сложной задачей. Придется отслеживать все пункты в массиве большого размера, страницы, которые пользователь просматривает, и написать функцию, которая будет вычислять, на основе текущей странице, пункт, который был выбран и какую кнопку нажали. Меню системы это всё делает за вас.&lt;br /&gt;
&lt;br /&gt;
'''Заметки:'''&lt;br /&gt;
*Пункты управления, которые не доступны, не рисуются. Например, на первой странице вы не можете нажать &amp;quot;назад&amp;quot;, а на последней странице вы не можете нажать &amp;quot;вперед&amp;quot;. Несмотря на это, меню пытается удерживать каждый интерфейс последовательно. Таким образом, визуально, каждая навигационная клавиша всегда на одной и той же позиции.&lt;br /&gt;
*Если указать таймаут меню, то переключение между страницами не воздействует на общее время открытия меню. Например, если мы установим таймаут на 20 секунд, то каждое листание страницы будет устанавливать таймайт равный (20 - время, уже затраченное на просмотр). Только переоткрытие меню позволит вернуть таймаут времени на 20 секунд.&lt;br /&gt;
*Если отключить кнопку выхода, то не смотря на это, пункты 8 и 9 будут всё так же &amp;quot;назад&amp;quot; и вперед, соответственно.&lt;br /&gt;
*Мы не освобождаем дескриптор (Handle) меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, потому что наше меню глобальное/статическое, и нам не нужно пересоздавать его каждый раз.&lt;br /&gt;
*На изображении отображена кнопка &amp;quot;Назад&amp;quot;. В SourceMod версии 1011 и выше, кнопка &amp;quot;Назад&amp;quot; была переименована в &amp;quot;Предыдующая&amp;quot;, и &amp;quot;Назад&amp;quot; зарезервированно для &amp;quot;ExitBack&amp;quot; функциональности. (актуально для ENG версии, на ру вроде бы оба пункта &amp;quot;назад&amp;quot;. прим. переводчик)&lt;br /&gt;
&lt;br /&gt;
=Голосование=&lt;br /&gt;
SourceMod также имеет API для отображения меню как голосования для больше чем одного клиента. SourceMod автоматически обрабатываем выбранные пункты and randomly picking a tie-breaker (не знаю как перевести. прим. переводчика). Так же API голосования добавляет два новых значения для &amp;lt;tt&amp;gt;MenuAction&amp;lt;/tt&amp;gt;, которые во время отображения меню всегда вызываются.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteStart&amp;lt;/tt&amp;gt;: Вызывается после &amp;lt;tt&amp;gt;MenuAction_Start&amp;lt;/tt&amp;gt;, когда голосование оффициально началось.&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;: Вызывается после того, как все клиенты проголосовали или отменили свои меню голосований. Выбранный предмет может быть получен через &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. Вызывается '''перед''' &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. Важно заметить, что это не замена &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;, это разные вещи. Нельзя освобождать меню в &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;. '''Заметка:''' не вызывается, если используется &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
*&amp;lt;tt&amp;gt;MenuAction_VoteCancel&amp;lt;/tt&amp;gt;: Вызывается, если меню было отмеено в процессе голосования. Если вызвалось, то &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; или обратный вызов (callback) для результата голосований не будет вызваны, но &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; будет после этого.  Причина отмены голосования находится в &amp;lt;tt&amp;gt;param1&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Система меню - это то же самое меню, только с двумя дополнительными свойствами:&lt;br /&gt;
*Только одно голосование может быть активно. Вы должны проверять, не запущено ли голосование (&amp;lt;tt&amp;gt;IsVoteInProgress&amp;lt;/tt&amp;gt;()), иначе &amp;lt;tt&amp;gt;VoteMenu&amp;lt;/tt&amp;gt;() не сработает.&lt;br /&gt;
*Если клиент во время голосования отключится, то его голос будет недействительным. &lt;br /&gt;
&lt;br /&gt;
Пример внизу показывает, как создать функцию &amp;lt;tt&amp;gt;DoVoteMenu()&amp;lt;/tt&amp;gt;, которая будет спрашивать, хотят ли они сменить карту на предложенную.&lt;br /&gt;
&lt;br /&gt;
==Простое Голосование==&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;да&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;нет&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Продвинутое Голосование==&lt;br /&gt;
Если вам нужно больше информации о результатах голосовании, чем вам дает &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt;, вы можете указать отдельный обратный вызов (callback). Новый обратный вызов будет давать больше информации, но за опреденную цену: &amp;lt;tt&amp;gt;MenuAction_VoteEnd&amp;lt;/tt&amp;gt; не будет вызвано, и вам придется самим решать, как обработать результат. Это делается с помощью &amp;lt;tt&amp;gt;SetVoteResultCallback&amp;lt;/tt&amp;gt;().&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Handle_VoteResults(Handle:menu, &lt;br /&gt;
			num_votes, &lt;br /&gt;
			num_clients, &lt;br /&gt;
			const client_info[][2], &lt;br /&gt;
			num_items, &lt;br /&gt;
			const item_info[][2])&lt;br /&gt;
{&lt;br /&gt;
	/* Проверяем, нет ли сразу двух победителей */&lt;br /&gt;
	new winner = 0;&lt;br /&gt;
	if (num_items &amp;gt; 1&lt;br /&gt;
	    &amp;amp;&amp;amp; (item_info[0][VOTEINFO_ITEM_VOTES] == item_info[1][VOTEINFO_ITEM_VOTES]))&lt;br /&gt;
	{&lt;br /&gt;
		winner = GetRandomInt(0, 1);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	new String:map[64];&lt;br /&gt;
	GetMenuItem(menu, item_info[winner][VOTEINFO_ITEM_INDEX], map, sizeof(map));&lt;br /&gt;
	ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu);&lt;br /&gt;
	SetVoteResultCallback(menu, Handle_VoteResults);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=ExitBack=&lt;br /&gt;
ExitBack - специальный термин для обозначения &amp;quot;ExitBack Кнопки&amp;quot;. По умолчанию эта кнопка отключена.  Обычно нумерованные меню не имеют пункта &amp;quot;Назад&amp;quot; для первой страници. Если &amp;quot;ExitBack&amp;quot; кнопка включена, появится кнопка &amp;quot;Назад&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
Выбор опции &amp;quot;ExitBack&amp;quot; для меню в обратном вызове будет в действии &amp;lt;tt&amp;gt;MenuCancel_ExitBack&amp;lt;/tt&amp;gt; и &amp;lt;tt&amp;gt;MenuEnd_ExitBack&amp;lt;/tt&amp;gt;. Функциональность этого такая же, как и у простого выхода из меню; дополнительная функциональность должна быть определена через обратный вызов.&lt;br /&gt;
&lt;br /&gt;
=Освобождение Дескрипторов (Handle) меню=&lt;br /&gt;
Важно закрывать дескриптор меню в &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt;. &amp;lt;tt&amp;gt;MenuAction_End&amp;lt;/tt&amp;gt; вызывается когда меню закрылось и больше не нужно.&lt;br /&gt;
&lt;br /&gt;
=Переводы=&lt;br /&gt;
Есть возможность динамически переводить меню для каждого игрока через &amp;lt;tt&amp;gt;MenuAction_DisplayItem&amp;lt;/tt&amp;gt; callback. Специальный натив, &amp;lt;tt&amp;gt;RedrawMenuItem&amp;lt;/tt&amp;gt;, используется чтобы преобразовать текст внутри вызова. Давайте переделаем наш пример голосования с переводами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Handle_VoteMenu(Handle:menu, MenuAction:action, param1, param2)&lt;br /&gt;
{&lt;br /&gt;
	if (action == MenuAction_End)&lt;br /&gt;
	{&lt;br /&gt;
		/* Вызывается после VoteEnd */&lt;br /&gt;
		CloseHandle(menu);&lt;br /&gt;
	} else if (action == MenuAction_VoteEnd) {&lt;br /&gt;
		/* 0=да, 1=нет */&lt;br /&gt;
		if (param1 == 0)&lt;br /&gt;
		{&lt;br /&gt;
			new String:map[64];&lt;br /&gt;
			GetMenuItem(menu, param1, map, sizeof(map));&lt;br /&gt;
			ServerCommand(&amp;quot;changelevel %s&amp;quot;, map);&lt;br /&gt;
		}&lt;br /&gt;
	} else if (action == MenuAction_DisplayItem) {&lt;br /&gt;
		/* Получаем отображаемую строку, будем использовать как фразу перевода */&lt;br /&gt;
		decl String:display[64];&lt;br /&gt;
		GetMenuItem(menu, param2, &amp;quot;&amp;quot;, 0, _, display, sizeof(display));&lt;br /&gt;
&lt;br /&gt;
		/* Переводим строку на язык клиента */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, display, param1);&lt;br /&gt;
&lt;br /&gt;
		/* Перерисовываем текст */&lt;br /&gt;
		return RedrawMenuItem(buffer);&lt;br /&gt;
	} else if (action == MenuAction_Display) {&lt;br /&gt;
		/* Дескриптор панели будет вторым параметром */&lt;br /&gt;
		new Handle:panel = Handle:param2;&lt;br /&gt;
		&lt;br /&gt;
		/* Get the map name we're changing to from the first item */&lt;br /&gt;
		/* Получаем название карты, на которую меняем, из первого пункта */&lt;br /&gt;
		decl String:map[64];&lt;br /&gt;
		GetMenuItem(menu, 0, map, sizeof(map));&lt;br /&gt;
		&lt;br /&gt;
		/* Переводим нашу фразу */&lt;br /&gt;
		decl String:buffer[255];&lt;br /&gt;
		Format(buffer, sizeof(buffer), &amp;quot;%T&amp;quot;, &amp;quot;Change map to?&amp;quot;, client, map);&lt;br /&gt;
&lt;br /&gt;
		SetPanelTitle(panel, buffer);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DoVoteMenu(const String:map[])&lt;br /&gt;
{&lt;br /&gt;
	if (IsVoteInProgress())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new Handle:menu = CreateMenu(Handle_VoteMenu, MenuAction_DisplayItem|MenuAction_Display);&lt;br /&gt;
	SetMenuTitle(menu, &amp;quot;Сменить карту на: %s?&amp;quot;, map);&lt;br /&gt;
	AddMenuItem(menu, map, &amp;quot;Yes&amp;quot;);&lt;br /&gt;
	AddMenuItem(menu, &amp;quot;no&amp;quot;, &amp;quot;No&amp;quot;);&lt;br /&gt;
	SetMenuExitButton(menu, false);&lt;br /&gt;
	VoteMenuToAll(menu, 20);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Template_talk:Languages:ru&amp;diff=11203</id>
		<title>Template talk:Languages:ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Template_talk:Languages:ru&amp;diff=11203"/>
		<updated>2021-07-20T13:23:42Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Created page with &amp;quot;&amp;quot;Language:&amp;quot;  Won't localize when changing language :[&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;quot;Language:&amp;quot; &lt;br /&gt;
Won't localize when changing language :[&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/ru&amp;diff=11201</id>
		<title>Installing SourceMod/ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/ru&amp;diff=11201"/>
		<updated>2021-07-19T17:19:08Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
Установка SourceMod очень проста, и он может быть добавлен практически без изменений конфигурации.&lt;br /&gt;
&lt;br /&gt;
=Требования=&lt;br /&gt;
Веб-браузер с графическим интерфейсом для получения сжатых архивов Metamod и SourceMod.&lt;br /&gt;
Инструмент для копирования архива на ваш выделенный сервер.&lt;br /&gt;
&lt;br /&gt;
SourceMod требует [[Metamod:Source]] 1.9.0 или выше (рекомендуется использовать последнюю версию). [http://www.metamodsource.net/ Нажмите здесь], чтобы посетить главную страницу Metamod:Source. Инструкции по установке SourceMM вручную можно найти [[Installing Metamod:Source|здесь]].&lt;br /&gt;
&lt;br /&gt;
SourceMod будет работать практически с любым модом, созданным с использованием Source SDK.&lt;br /&gt;
&lt;br /&gt;
Valve иногда вносит изменения в свои игры, которые нарушают SourceMod между выпусками. &lt;br /&gt;
 Когда это происходит, вам может потребоваться установить новую версию Metamod и SourceMod.  Вы можете узнать, требуется ли это, на странице [[Required Versions (SourceMod)|необходимых версий]].&lt;br /&gt;
&lt;br /&gt;
=Загрузка/установка=&lt;br /&gt;
==Локальный сервер==&lt;br /&gt;
Чтобы установить [http://www.sourcemod.net/downloads.php SourceMod] в локальное хранилище, просто извлеките архив &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) или &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) в папку вашего мода (например, &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; для Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; для Day of Defeat и так далее).&lt;br /&gt;
&lt;br /&gt;
==Выделенный сервер==&lt;br /&gt;
Чтобы установить SourceMod на выделенный сервер, сначала распакуйте архив &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) или &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) на локальный компьютер (например, на рабочий стол).  Вы увидите папку &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Используя такой инструмент, как &lt;br /&gt;
[https://encrypted.google.com/#q=FTP FTP], найдите папку вашего мода (например, &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; для Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; для Day of Defeat:Source и так далее).  Под этой папкой должна находиться папка &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; (если это не так, Metamod:Source, вероятно, не установлен).  Из локальной папки &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; загрузите всё содержимое в папку выделенного сервера &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;.  После этого в папке выделенного сервера &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; должна появиться папка &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если у вас возникли трудности с этими шагами, вам необходимо ознакомиться с FTP и управлением сервером.  Однако вы также можете обратиться за помощью к своему провайдеру сервера.  Некоторые провайдеры также имеют веб-интерфейсы для управления вашим сервером.&lt;br /&gt;
&lt;br /&gt;
В качестве альтернативы, если вы скопировали tar.gz в папку srcds, выполните следующие действия из подпапки cstrike:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Проверка установки=&lt;br /&gt;
Расположение папок должно выглядеть следующим образом:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[мод]&amp;lt;/tt&amp;gt; - Папка вашего мода&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
После загрузки/копирования SourceMod и настройки Metamod:Source полностью перезапустите ваш сервер.  Если он локальный, выключите его и перезапустите.  Если сервер выделенный, вам может понадобиться обратиться за помощью к провайдеру.  Тем не менее, часто безопасно отдать команду &amp;quot;quit&amp;quot; через [[rcon]], поскольку большинство провайдеров автоматически перезапустят ваш сервер.&lt;br /&gt;
&lt;br /&gt;
Сначала в [[консоль сервера]] (не клиентской консоли) введите:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если установка прошла успешно, вы увидите что-то вроде:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Найден 1 плагин:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) от AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После этого вы должны иметь возможность использовать корневую команду консоли SourceMod, которая вызывается просто:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 Информация о версии SourceMod:&lt;br /&gt;
    SourceMod версии: 1.1.0.2489&lt;br /&gt;
    Движок SourcePawn: SourcePawn 1.1, jit-x86 (сборка 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: вер.1 = 4, вер.2 = 2&lt;br /&gt;
    Составлено на: 5 сентября 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Наконец, если вы уже настроили административного пользователя (если нет, то прочтите [[Adding Admins (SourceMod)|эту страницу]]), вы можете протестировать игровое меню, присоединившись к серверу, и в клиентской консоли наберите следующее:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
Должно появиться всплывающее меню со всеми опциями.&lt;br /&gt;
&lt;br /&gt;
=Устранение проблем=&lt;br /&gt;
Если установка не удалась, обычно вы увидите одну из четырёх причин.  &lt;br /&gt;
&lt;br /&gt;
==Metamod сообщает NOFILE или FAILED==&lt;br /&gt;
Если &amp;quot;meta list&amp;quot; ответит что-то вроде:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Найден 1 плагин:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Скорее всего, либо файлы расположены в неправильном месте, либо файл не удалось загрузить.  Для получения дополнительной информации используйте следующую команду (за исключением использования правильного номера списка):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==В списке Metamod нет плагинов==&lt;br /&gt;
Если &amp;quot;meta list&amp;quot; ответит что-то вроде:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Найдено 0 плагинов:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Существует несколько причин этой ошибки.&lt;br /&gt;
&lt;br /&gt;
# Наиболее распространённой причиной является то, что файл sourcemod.vdf не может быть расположен в папке addons/metamod.  Убедитесь, что файл sourcemod.vdf присутствует в этой папке.&lt;br /&gt;
# Если файл sourcemod.vdf присутствует, убедитесь, что вы используете правильную сборку Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod ничего не пишет==&lt;br /&gt;
Если &amp;quot;meta list&amp;quot; вообще не имеет ответа, значит Metamod:Source установлен неправильно. [[Installing Metamod:Source|Эта страница вики]] может подсказать вам, как решить эту проблему.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/ru&amp;diff=11200</id>
		<title>Installing SourceMod/ru</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/ru&amp;diff=11200"/>
		<updated>2021-07-19T16:14:25Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Created page with &amp;quot;{{Languages|Installing_SourceMod}}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=User:Kleiner&amp;diff=11199</id>
		<title>User:Kleiner</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=User:Kleiner&amp;diff=11199"/>
		<updated>2021-07-19T14:58:49Z</updated>

		<summary type="html">&lt;p&gt;Kleiner: Created page with &amp;quot;English-Russian Translator &amp;lt;br&amp;gt; Steam:http://steamcommunity.com/profiles/76561198158864042 &amp;lt;br&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;English-Russian Translator&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Steam:http://steamcommunity.com/profiles/76561198158864042&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kleiner</name></author>
		
	</entry>
</feed>