Плагин: команды, события, таймеры, чат, переводы
Плагин — это один файл .ts в папке плагинов проекта. Работу он делает на
верхнем уровне файла: называет себя, добавляет команды и подписывается на
события. То, чем он пользуется из ядра, — plugin, server, Player,
print — строки импорта не требует (автоимпорты).
plugin({ name: "Welcome", version: "1.0.0", author: "you" });
server.addCommand("/hp", ({ player }) => showHp(player));
server.addEventListener("putInServer", (event) => {
print(event.player, "Добро пожаловать на сервер!");
});
function showHp(player: Player) {
print(player, `${player.name}, у тебя ${player.health} HP`);
}
Всё, что есть на этой странице, — API ядра, @amxts/core, и оно импортируется
само.
Автоимпорты
Плагин пользуется API ядра и тем, что дают модули проекта, не импортируя их. Сборка находит имена, которые файл использует, но не объявляет, и добавляет импорт каждого, которое знает:
semiclip.rule = (player, target) => player.team == target.team;
server.addCommand("/through", ({ player }) => showThrough(player));
function showThrough(player: Player) {
const names = semiclip.passesThrough(player).map(other => other.name);
print(player, `Вы проходите сквозь: ${names.join(", ")}`);
}
server, Player и print — ядра; semiclip даёт @amxts/resemiclip в
проекте, где amxts.config.ts его перечисляет. Импорты встают после последней строки
файла, поэтому ошибка по-прежнему указывает на строку, которую вы написали.
- Что импортируется само: всё, что
@amxts/coreэкспортирует для плагинов, —Player,Entity,server,game,print,lang,effects,Cvar,Forward,Storage, таймеры, типы событий (TakeDamageEvent), имена полей и флагов (Team,HideHud) — и имя каждого модуля (menus,configs,semiclip). Не нативы и константы (@amxts/core/natives,@amxts/core/constants), не файлы (@amxts/core/fs), не ваши собственные файлы: плагин, которому они нужны, говорит об этом импортом. - Своё имя важнее. Переменная, параметр или функция с именем
server— ваши там, где они видны; автоимпорт — только для мест, где их не видно. - Явный импорт тоже работает:
import { Player } from "@amxts/core",import * as menus from "@amxts/menu-core". Его пишет плагин, которому модуль нужен под другим именем, и для того, что само не импортируется. - Что называет импорт. API ядра — по имени пакета:
@amxts/core,@amxts/core/natives,@amxts/core/constants,@amxts/core/fs,@amxts/core/os; модуль — по имени его пакета; ваши собственные файлы — через~/, это папка плагинов:import { twice } from "~/lib/math"— этоplugins/lib/math.ts, из любой подпапки.~/называет только ваши файлы: импорт API ядра через него не собирается. - Редактор знает те же имена:
npx amxts prepare— его первым шагом запускаютdev,build,typecheckиnpm install— пишет.amxts/imports.d.ts, где они глобальные, поэтому автодополнение, переход к определению и проверка типов работают без импорта. Файл пишется заново, когда после изменения модулей вamxts.config.tsзапускается команда. - Как выключить:
imports: { autoImport: false }вamxts.config.ts; тогда каждый плагин импортирует то, чем пользуется, сам.
// amxts.config.ts
export default defineConfig({
modules: ["@amxts/menu-core"],
imports: { autoImport: false },
});
Модуль говорит, что даёт, в своём defineModule, — себя пространством
имён, imports: [{ from: "@amxts/menu-core", as: "menus" }], или один из
своих экспортов под его именем, [{ from: "@amxts/resemiclip", name: "semiclip" }]
(свой модуль). Два источника одного имени
останавливают сборку, и она называет оба.
Собирается только то, чем пользуются
Плагин получает импорт только того, чем пользуется, поэтому модуль, которым
не пользуется ни один плагин, не попадает ни в один из них. Сборка идёт
дальше: собственный плагин модуля она собирает и пишет в plugins.ini,
только когда модулем пользуется хоть один плагин проекта — через автоимпорт
или явный импорт, сам или через файл, который он импортирует. Модуль из
amxts.config.ts, которым не пользуется никто, в сборку не попадает, и
сборка об этом говорит:
i module "resemiclip" is installed but no plugin uses it - left out
Модулем, чьи нативы вызывают Pawn-плагины, — mc_* у menu-core, cfg_* у
config-core — пользуются плагины, которых сборка не видит. Его оставляет
pawn:
// amxts.config.ts
export default defineConfig({
modules: ["@amxts/config-core", "@amxts/menu-core"],
pawn: ["@amxts/menu-core"], // Pawn-плагины вызывают mc_*: собирается, даже если им не пользуется ни один плагин
});
То, что модуль требует, идёт вместе с ним. В amxts dev сохранение, после
которого плагин начал пользоваться модулем, собирает модуль вместе с
плагином; модуль, которым больше не пользуется ни один плагин, уходит из
plugins.ini.
Описание плагина
plugin({ name: "Welcome", version: "1.0.0", author: "you", description: "Приветствует игроков" });
Имя, версия и автор, под которыми сервер показывает плагин: их выводит
amxts_plugins в консоли сервера. description можно не писать. plugin
вызывается один раз, в начале файла.
Команды с v0.2
interface KickArgs {
target: Player;
reason?: string;
}
server.addCommand<KickArgs>("/kick <target> [reason]", ({ player, target, reason }) => {
print(0, `${player.name} кикает ${target.name}`);
target.kick(reason ?? "Кикнут админом");
}, { access: "kick", description: "Кикнуть игрока" });
server.addCommand("/hp", ({ player }) => print(player, `${player.health} HP`));
server.addCommand("myplugin_heal <amount>", ({ player, amount }) => print(player, `лечение ${amount}`)); // без слэша - команда консоли
server.addCommand("say rules", ({ player }) => showRules(player)); // строка чата без слэша
Первый аргумент — использование команды: её имя, затем аргументы,
<name> — тот, что игрок должен набрать, [name] — тот, что может
пропустить. Обработчик получает их под этими именами, вместе с player —
игроком, который набрал команду.
Типы аргументов — интерфейс, с именем, переданный первым, как
addCommand<KickArgs> (тип, написанный на месте, <{ ... }>, читается хуже
и не годится второй раз). Поле говорит, как читается его слово:
| тип | слово | например |
|---|---|---|
string | как есть; последний аргумент забирает остаток строки | reason в /kick bob being rude — "being rude" |
number | число | /give 5 |
Player | #userid, имя целиком или его часть, в любом регистре | /kick bo, /kick #12 |
"on" | "off" | одно из этих слов | /mode on |
[name] в использовании — это name?: в интерфейсе, а <name> — поле без
?. Имя, которое есть в использовании, но нет в интерфейсе, — или наоборот, — не собирается, и ошибка говорит какое. Без интерфейса каждый аргумент —
string: ({ amount }) выше — текст.
Если слово не то, что команда принимает, — число, которое не число, игрок, чьё имя ни с чем не совпало, — игрок получает в чат использование команды, и обработчик не запускается. На часть имени, которая есть у нескольких игроков, он получает их имена, чтобы набрать побольше. Лишнее слово получает тот же ответ, если только последний аргумент не текст: текст забирает остаток строки.
- Имя со
/— команда чата. Её запускают иsay /hp, иsay_team /hp, а сама строка в чате не появляется. - Имя без
/— команда консоли: игрок набираетmyplugin_heal 50у себя в консоли. "say <фраза>"срабатывает, когда игрок пишет в чат ровно эту фразу:"say rules"— наrules, но не наrules please. Аргументов у неё нет.
Регистр в имени команды не важен: /HP запускает /hp.
access — право админа, которое нужно игроку, как его даёт users.ini:
"kick", "ban", "slay", "map", "cvar", "rcon", "levelA" …
"levelH" и остальные — редактор их подсказывает. У кого права нет, для того
команда ничего не делает, а его строка уходит в чат обычным сообщением.
description — то, что показывает amx_help.
Права самого игрока — player.access, массив тех же имён:
player.access.includes("ban").
Команда помощи
server.commands перечисляет команды, которые добавил плагин, — у каждой
usage, description и access, — это и печатает /help:
server.addCommand("/help", ({ player }) => {
for (const command of server.commands) {
if (command.access == null || player.access.includes(command.access)) print(player, `${command.usage} - ${command.description}`);
}
});
Команды сервера
interface ResetArgs {
what?: "scores" | "all";
}
server.addServerCommand<ResetArgs>("myplugin_reset [what]", ({ what }) => {
console.log(`сброс: ${what ?? "all"}`);
});
Команда консоли сервера: её набирают там, присылают по rcon или запускает
другой плагин через server_cmd. Её использование и аргументы читаются так
же, как у команды игрока; игрок её не набирает, поэтому player у
обработчика нет. То, что он печатает через console.log, уходит тому, кто
прислал команду, — по rcon это ответ rcon.
События сервера
server.addEventListener("putInServer", (event) => {
print(event.player, "Добро пожаловать!");
});
server.addEventListener("disconnected", (event) => {
console.log(`${event.player.name} вышел: ${event.reason}`);
});
server.addEventListener("configsExecuted", loadSettings); // сервер выполнил свои конфиги
server.addEventListener работает как addEventListener в браузере: первый
аргумент — имя события, второй — функция, которую сервер вызывает каждый раз,
когда событие случается. От имени зависит тип event, поэтому
(event) => ... пишется без аннотации, а редактор дополняет имена и
подчёркивает опечатку. Поля типизированы: игрок — Player, текст —
string, да/нет — boolean (event.dropped).
| Событие | Когда |
|---|---|
"init" | плагин запускается, когда загружены все плагины |
"pluginsLoaded" | все плагины запустились: время создать форвард, который услышат другие плагины |
"configsExecuted" | карта загрузилась, и сервер выполнил свои конфиги — server.cfg, amxx.cfg, конфиг карты: время читать квары |
"precache" | грузится карта: время прекешировать модели и звуки |
"changeLevel" | карта вот-вот сменится |
"end" | плагин останавливается: смена карты или выключение сервера |
"connect" | игрок подключается и ещё не в игре |
"authorized" | стал известен Steam ID игрока (event.steamId) |
"putInServer" | игрок вошёл в игру |
"disconnected" | игрок ушёл (event.dropped, event.reason) |
"playerChange" | изменилось поле, которое плагины добавили в Player (общие поля игрока) |
"command" | игрок отправил команду консоли |
"impulse" | игрок отправил импульс: 100 — фонарик, 201 — спрей |
"suicide" | игрок набрал kill в консоли |
"frame" | каждый кадр сервера |
В "connect", "authorized" и "putInServer" игрок ещё не появился на
карте, поэтому event.player — это Client: у него есть id, name,
ip, steamId, access, team, muted и command, но нет здоровья и
оружия. У Player есть всё, что есть у Client, поэтому функция, которой
нужно только это, принимает Client и работает с обоими.
Весь список показывает редактор; его подсказка называет форвард AMX Mod X,
который под событием (client_putinserver). То, что происходит с игроком в
игре, — он «думает» каждый кадр, меняет свои данные, касается чего-то, — это
игровые события: game.addEventListener("preThink", ...),
"userInfoChange", "touch".
Обработчик, написанный отдельной функцией, указывает тип события — это имя
форварда словами: ClientPutinserverEvent, ClientDisconnectedEvent,
ClientImpulseEvent.
server.addEventListener("impulse", onImpulse);
function onImpulse(event: ClientImpulseEvent) {
if (event.impulse == 100) print(event.player, "Фонарик!");
}
server.removeEventListener("impulse", onImpulse) отписывает обработчик:
передайте ту же функцию, что добавляли.
Имя пишется строкой прямо в вызове: server.addEventListener(name, ...) с
именем в переменной — ошибка сборки, потому что по имени выбирается тип
события.
Обработчик — замыкание, как в JavaScript: он видит переменные функции, в которой написан, и они живут, пока живёт он.
server.addEventListener("putInServer", (event) => {
const player = event.player;
setTimeout(() => print(player, `Рады видеть, ${player.name}!`), 2000);
});
Сообщения клиентам с v0.2
server.addMessageListener("death", (event) => {
if (event.headshot) print(0, `${event.killer?.name} - headshot - ${event.victim?.name}`);
});
Любое сообщение, которое сервер шлёт клиенту, — строка чата, значок HUD, часы
раунда, сообщение об убийстве, — слышно через server.addMessageListener до
того, как оно ушло, с аргументами в типизированных полях. Подробности и имена
всех сообщений — на странице Сообщения.
События игры
game.addEventListener("newRound", () => console.log("новый раунд"));
game.addEventListener("takeDamage", (event) => {
if (event.attacker.team == event.player.team) event.preventDefault();
});
То, что происходит в игре, — урон, возрождения, смерти, конец раунда,
покупки — это события на game, так же как события сервера — на server.
Обработчик может остановить действие игры или ответить вместо неё. Их приносят
модули ReAPI и Ham Sandwich, и большинство слышно и без ReAPI; подробно — в
Событиях игры.
Таймеры
const handle = setTimeout(() => print(player, "Вперёд!"), 2000); // один раз, через 2 с
clearTimeout(handle); // отменить до срабатывания
function countdown(player: Player) {
let left = 3;
const ticking = setInterval(() => {
print(player, `${left}`);
left--;
if (left == 0) clearInterval(ticking);
}, 1000);
}
Как в браузере: setTimeout вызывает функцию один раз через заданное число
миллисекунд, setInterval — раз в столько-то миллисекунд, и оба возвращают
дескриптор, который принимают clearTimeout и clearInterval. Функция может
читать и менять переменные вокруг себя. Функция без параметров передаётся как
есть: setTimeout(startVote, 3000).
Внутри async-функции так же ждёт await sleep(1000)
(async).
Как любой таймер AMX Mod X, таймер ждёт не меньше 0,1 секунды и срабатывает на кадре сервера:
setTimeout(f, 10) выполнится примерно через 100 мс.Время и случайность
Date.now(); // миллисекунды с 1970 года
new Date().getHours(); // час по часам сервера
new Date().getUTCHours(); // час по UTC
const start = performance.now(); // миллисекунды с точностью до микросекунды, для замеров
Math.random(); // 0 <= x < 1, при каждом запуске новая последовательность
game.time; // часы игры: секунды с начала карты
Date работает как в JavaScript: new Date() — сейчас, геттеры UTC
(getUTCHours, ...) и местные (getHours, getDate, getMonth,
getFullYear, getTimezoneOffset, ...) — местные по часовому поясу
сервера. Дата становится текстом так, как её пишет JavaScript, по часовому
поясу сервера:
const now = new Date();
now.toString(); // "Tue Sep 29 2026 14:05:09 GMT+0300"
now.toLocaleString(); // "9/29/2026, 2:05:09 PM"
now.toLocaleString("ru-RU"); // "29.09.2026, 14:05:09"
now.toLocaleDateString("de"); // "29.9.2026"
now.toLocaleTimeString("en-GB"); // "14:05:09"
toLocaleString, toLocaleDateString и toLocaleTimeString принимают
язык: en-US, если он не назван, en-GB, de, fr, ru и языки, которые
пишутся как ru (uk, be, kk); другой пишется как en-US.
game.time — часы, по
которым идут поля сущностей, хранящие момент: grenade.damageTime = game.time + 1.
Таймеры атаки — nextPrimaryAttack оружия, nextAttack игрока — считаются
от текущего момента: weapon.nextPrimaryAttack = 1 — это через секунду.
Чат и консоль
print(player, "Это видишь только ты");
print(0, "Это видят все");
print(player, "Здоровье восстановлено!", "center"); // "chat" (по умолчанию), "center", "console", "notify"
print({ id: 0, variant: "center" }, "Вперёд!"); // всем, посреди экрана
console.log("в консоль сервера");
print принимает игрока, id игрока или 0 — всем. Третий аргумент — где
показать текст: "chat", "center" (посреди экрана), "console" (консоль
игрока) или "notify" (тоже консоль; на экране игра показывает его только с
developer 1).
console.log пишет строку в консоль сервера, после [amxts];
console.warn и console.error помечают свои строки как предупреждение и
ошибку. Каждый принимает строку или Error — его он пишет со стеком
(ошибки).
Цвета
Цвет в тексте — метка: ! и буква.
print(player, "!g[Магазин]!y Ты купил !rброню");
| Метка | Цвет | Чат | Меню |
|---|---|---|---|
!y | жёлтый, обычный цвет чата | да | да |
!r | красный | да | да |
!d | серый | да | да |
!g | зелёный | да | нет |
!b | синий | да | нет |
!t | цвет команды читающего | да | нет |
!w | белый | нет | да |
!R | остаток строки — к правому краю | нет | да |
Буква означает один и тот же цвет в чате и в меню.
Каждое место рисует свои метки, а чужие убирает из текста, и никто об этом не предупреждает:
!w и !R — для меню, !g, !b и !t — для чата. У
"center", "console" и "notify" цветов нет: там текст показывается как
написан, вместе с метками. Регистр в метках важен: !R — не красный.Красный, синий, серый и
!t рисуются цветом команды, а он у сообщения в чате
один: побеждает первый из них в строке, остальные показываются им же.
"!rКрасный !bСиний" выводит оба слова красным.Ошибки с v0.2
Вызов, который падает, — throw, который никто не поймал, x! на null,
индекс за границей, переполнение стека, — заканчивает этот вызов, а не
плагин: следующее событие, команда или таймер выполняются как обычно.
Консоль сервера называет ошибку и место, где она случилась.
function price(item: Item | null): number {
return item!.price;
}
function onSelect(): number {
return price(null);
}
Плагин, который компилирует amxts dev, ведёт запись своих вызовов: консоль
называет каждый вызов, который привёл к ошибке, — с файлом, строкой и
столбцом в твоём TypeScript — и под первым вызовом твоего собственного кода
показывает саму строку:
[amxts] myplugin.aot: TypeError: Unexpected 'null' (not assigned or failed cast)
at price (plugins/myplugin/shop.ts:26:9)
26 | return item!.price;
at onSelect (plugins/myplugin/shop.ts:30:15)
at timerFired (node_modules/@amxts/core/as/facade.ts:3772:2)
Плагин, который компилирует amxts build, — и тот, что сервер сам
компилирует из .ts в своей папке plugins, — такой записи не ведёт: она
замедляла бы нагруженный плагин
(производительность). Консоль называет ошибку, а для
throw, x! или неудавшейся проверки — место выражения, которое упало;
переполнение стека говорит только своё сообщение:
[amxts] myplugin.aot: TypeError: Unexpected 'null' (not assigned or failed cast) (plugins/myplugin/shop.ts:26:9)
У пойманной ошибки тот же стек: error.stack — этот текст, от места, где
ошибку создали, а console.error(error) его пишет. В плагине, который
компилирует amxts build, error.stack — первая строка ошибки.
try {
buy(player, item);
} catch (error) {
console.error(error); // "Error: ..." и, в сборке для разработки, вызовы под ней
}
Строка под первым вызовом показывается, когда сервер может прочитать твой
.ts, как с amxts dev. Вызовы внутри собственной библиотеки
AssemblyScript — методы массива, String — показывают имя без места.
Меню с v0.2
Настоящие меню — магазин, меню админа, голосование — это то, для чего есть официальный модуль menu-core: меню в файлах INI, YAML или JSON, которые владелец сервера правит без сборки, пункты, которые показываются и доступны по условиям,
%плейсхолдеры%, списки игроков и своих строк, меню, в которые
добавляют пункты Pawn-плагины, и редактор, который проверяет файл меню прямо
при наборе. Добавьте его командой npx amxts module add menu-core и
прочитайте Меню.Быстрое меню в коде
Menu — собственное меню AMX Mod X как объект: заголовок, пункты и
show(player). Оно входит в ядро и не требует модуля — для одного-двух меню,
которые плагин собирает в коде.
interface ShopData {
category: string;
}
const shop = new Menu<ShopData>(({ data }) => `!yМагазин: ${data.category}`);
shop.addItem({
title: "Броня - !y$1000",
enabled: ({ player }) => player.armor < 100,
onSelect: ({ player }) => {
player.armor = 100;
},
});
shop.addItem({
title: ({ player }) => `Лечение !d(${player.health} HP)`,
visible: ({ player }) => player.isAlive,
enabled: ({ player }) => player.health < 100,
onSelect: ({ player, menu, data }) => {
player.health = 100;
menu.show(player, data); // остаётся открытым
},
});
server.addCommand("/shop", ({ player }) => shop.show(player, { category: "all" }));
- Пункт — объект: его
title,visible— показан ли он,enabled— можно ли его выбрать, иonSelect— что делает выбор. Безvisibleиenabledониtrue; скрытый пункт не занимает места, недоступный рисуется серым и ничего не делает. - Каждая функция получает контекст:
player— тот, кому показано меню;menu— само меню;data— то, что получилshow.Data, аргумент типа, говорит, что это, — меню, показанное без данных, его не передаёт. - Заголовок,
visibleиenabledмогут быть функцией или просто значением: они вычисляются при каждомshow, для этого игрока. - После выбора меню закрывается.
onSelect, который показывает его снова, оставляет его открытым. - Страницы, «Назад», «Дальше» и «Выход» — как у AMX Mod X: семь пунктов на странице, остальные на следующих.
- Цветовые теги — те же, что в чате и меню:
!yжёлтый,!rкрасный,!dсерый,!wбелый,!R— к правому краю.
Создайте меню один раз, на верхнем уровне файла, и показывайте сколько угодно раз. Настройки — второй аргумент:
const vote = new Menu("!yСледующая карта?", {
perPage: 0, // все пункты на одной странице, без «Назад» и «Дальше» (до 10)
exit: false, // без пункта «Выход»
numberColor: "!y", // номера пунктов жёлтые, а не красные
backText: "Назад",
nextText: "Дальше",
exitText: "Закрыть",
});
Меню AMX Mod X
Для полного контроля нативы меню AMX Mod X доступны целиком: menu_create,
menu_additem, menu_setprop, menu_display и остальные, с их
константами (MPROP_*, MEXIT_*) — см. нативы напрямую.
Обработчик передаётся именем public-функции, которое для функции делает
publicFor из @amxts/core/kit. Меню старого вида — текст и клавиши, на
которые оно отвечает, — это showMenu из того же набора.
Переводы
Текст на языке читающего берётся из словаря: файла в data/lang, где у
каждого языка своя секция, а у каждого ключа — своя строка.
[en]
MYPLUGIN_WELCOME = Welcome, ^4%s^1!
MYPLUGIN_ON = \d[\yOn\d]
[ru]
MYPLUGIN_WELCOME = Добро пожаловать, ^4%s^1!
MYPLUGIN_ON = \d[\yВкл\d]
lang.load("myplugin"); // data/lang/myplugin.txt
server.addEventListener("putInServer", (event) => {
print(event.player, lang.translate(event.player, "MYPLUGIN_WELCOME", [event.player.name]));
});
lang.translate(player, key, args) отдаёт строку ключа на языке игрока — его
setinfo lang, — а null вместо игрока даёт язык сервера (amx_language).
Языка, которого нет в словаре, заменяет язык сервера. %s, %d и %f
(%.1f, %02d) заполняются из args, а это строки: число передаётся текстом,
`${seconds}`. Место, на которое аргумента не хватило, остаётся как
написано, а ключ, которого нет ни в одном словаре, возвращается как есть.
AMX Mod X читает строку словаря до 511 байт — это около 255 букв кириллицы; остаток более длинной строки обрезается.
player.language — этот самый язык, для того, чего нет в словаре, — звука,
картинки: player.language == "ru". Это его setinfo lang или язык сервера,
если своего у него нет или amx_client_languages равен 0.
Словарь пишет цвета так, как AMX Mod X, — \y в меню, ^4 в чате, — а
lang.translate возвращает их метками: \y — это !y, \r — !r,
\d — !d, \w — !w, \R — !R, ^1 — !y, ^3 — !t, ^4 — !g.
Поэтому одна строка работает и в меню, и в чате; каждое место убирает метки,
которых не рисует (!w в чате). ^n — перенос строки.
HUD-сообщения
player.showHud("Раунд 3", { color: [255, 40, 40], y: 0.3, hold: 2 });
server.showHud("Для всех");
Текст на экране вне чата. Настройки, крупные буквы и HudLine — на странице
Игроки.
Звуки и прекэш
server.precache("myplugin/hit.wav");
server.precache("models/myplugin/box.mdl");
function hit(player: Player) {
player.emitSound("myplugin/hit.wav"); // слышат все рядом
player.emitSound("myplugin/hit.wav", { volume: 0.5, pitch: 120, channel: "voice" });
player.playSound("vox/one.wav"); // слышит только он, как рацию
}
Модель, спрайт или звук, которым пользуется игра, прекэшируется, пока грузится
карта: server.precache(path) на верхнем уровне файла прекэширует его тогда,
а в событии "precache" — сразу. К тому же он заставляет игроков скачать
файл. Звук пишется так, как его играет игра, — внутри sound/
("myplugin/hit.wav"); модель или спрайт — со своей папкой
("models/myplugin/box.mdl"). Возвращает файл как Resource — его
эффект принимает вместо спрайта или модели.
entity.emitSound(sample, options) проигрывает звук от сущности — и от игрока
тоже: его слышат все рядом, и он затихает с расстоянием. У каждой настройки
есть значение по умолчанию:
| Настройка | По умолчанию | Значение |
|---|---|---|
channel | "auto" | канал сущности: "auto", "weapon", "voice", "item", "body", "stream", "static"; новый звук на канале обрывает тот, что играет на нём, "auto" не обрывает никогда |
volume | 1 | от 0 до 1 |
attenuation | 0.8 | как быстро затихает с расстоянием: 0 слышен по всей карте, 2 — только вблизи |
pitch | 100 | в процентах: 50 — октавой ниже, до 255 |
player.playSound(sample) проигрывает звук одному игроку — он слышит его
одинаково, где бы ни стоял, как рацию.
"precache" не получает; его верхний уровень выполняется снова, и
Resource, запрошенный там, находит индекс, который карта дала файлу. Файл,
который никто из плагинов не прекэшировал, пока грузилась карта, ждёт
следующей карты.Сервер
server.map; // "de_dust2"
server.maxPlayers; // 32
server.configsDir; // "addons/amxmodx/configs"
server.dataDir; // "addons/amxmodx/data"
server.command("changelevel de_nuke"); // выполнится в консоли сервера
server.command выполняет строку в консоли сервера, как будто её набрали
там. Выполнить её в консоли игрока — player.command("say hi").
Ограничения
Чего amxts пока не умеет — и что писать вместо этого. Почти о каждом пункте ниже скажут сборка, редактор или консоль сервера; здесь можно посмотреть, что это значит.
События игры
То, что происходит в игре, — игрок получает урон, возрождается, умирает, покупает оружие, бросает гранату, раунд заканчивается, — это события на game. Обработчик слышит событие до того, как игра что-то сделает, и может пропустить его, изменить, остановить или ответить вместо игры: