AMX Mod X: события, полезные функции и примеры
1 месяц, 2 недели назад | 0 ОтветыТема закреплена. Технический разбор — в первом сообщении. Вопросы и обсуждение — в ответах ниже.
12. События, полезные функции и примеры
Отлов событий CS 1.6
Как отловить событие победы контров, террористов и ничья(RoundDraw)
Отлов событий Так как Counter-Srtike игра где противостоят две команды, то исход может быть в разных трех случаях, а именно: Победа Контр Террористов Победа террористов Ничья Разумеется есть простой и надежный способ отловить данные события. Для этого необходимо знать как пользоваться функцией register_event, затем догадаться или подсмотреть, что необходимо отлавливать событие SendAudio Отлов события потери бомбы и ее установки. Отлов событий Отлов события установки бомбы или ее потери можно отловить несколькими разными способами. Сейчас же рассмотрю способ с помощью отлова функцией register_event. Структура события: Аргумент 1 — Координата X Аргумент 2 — Координата Y Аргумент 3 — Координата Z Аргумент 4 — флаг 1/0 если 1 то бомба была заложена, если ноль то или выкинута игроком или при убийстве и т.п. Отлов событий "конец карты" и "конец плагина" Отлов событий Недавно мне потребовалось отыскать событие которое происходит почти в самый последний момент перед сменой карты «конец карты», но при этом что бы игроки были еще на сервере. Найти такой момент было принципиально важно, а так же было важно и само событие смены карты или конец работы плагина ( выключение сервера ). Разобравшись с этими двумя событиями, поделюсь полученной информацией с вами, и начнем мы с события, которое наступает раньше. Отлов события нового раунда и старта раунда Отлов событий Недавно заметил, что не все понимают очевидную разницу между стартом раунда и началом нового раунда. А это принципиально разные вещи. У многих админов на серверах выключен mp_freezetime ( квар поставлен в 0) и по этому событие нового раунда и старта раунда сливается в одну секунду и разница не заметна. Но вы попробуйте поставить этому квару значение скажем в 10 секунд и выполните какую то команду при старте раунда, и сразу заметите очевидную разницу.
Как отловить событие победы контров, террористов и ничья(RoundDraw)
Как отловить событие победы контров, террористов и ничья(RoundDraw)
Отлов событий Так как Counter-Srtike игра где противостоят две команды, то исход может быть в разных трех случаях, а именно: Победа Контр Террористов Победа террористов Ничья Разумеется есть простой и надежный способ отловить данные события. Для этого необходимо знать как пользоваться функцией register_event, затем догадаться или подсмотреть, что необходимо отлавливать событие: SendAudio
Plays— the specified audio. An example of AudioCode could be "2%!MRAD_rounddraw".string— AudioCodeshort— Pitch И найти необходимые для отлова звуки в соответствующей папке %/cstrike/sound/radio/ Теперь давайте попробуем соединить все это воедино:
Name: SendAudio Structure: byte SenderID
register_event("SendAudio", "t_win", "a", "2&%!MRAD_terwin")
SendAudio — — Что отлавливаем ( событие проигрывания звука радио)t_win— — Функция которая будет вызывать при данном событииa— — Флаг А — говорит что событие глобальное
2&%!MRAD_terwin — Какой звук радио отлавливаем ( смотри синтаксис функции и описание события) Теперь как это выглядит все в месте для всех трех вариантов событий:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "[register_event] SendAudio who win" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR) // Событие победы терроров register_event("SendAudio", "t_win", "a", "2&%!MRAD_terwin") //Событие победы контров register_event("SendAudio", "ct_win", "a", "2&%!MRAD_ctwin") //Событие — ничья register_event("SendAudio", "draw", "a", "2&%!MRAD_rounddraw")
//Исполняемая функция при победе конров
public ct_win(){
client_print(0,print_chat,"CT WIN")
//Исполняемая функция при победе Терроров
public t_win(){
client_print(0,print_chat,"T WIN")
//Исполняемая функция при ничьей
public draw(){
client_print(0,print_chat,"DRAW") Вот так про
Отлов события потери бомбы и ее установки.
Отлов события потери бомбы и ее установки. Отлов событий Отлов события установки бомбы или ее потери можно отловить несколькими разными способами. Сейчас же рассмотрю способ с помощью отлова функцией register_event. Структура события: Аргумент 1 — Координата X Аргумент 2 — Координата Y Аргумент 3 — Координата Z Аргумент 4 — флаг 1/0 если 1 то бомба была заложена, если ноль то или выкинута игроком или при убийстве и т.п.
Пример:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "[register_event] BombDrop" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR) //Регистрируем событие и назначаем функцию //которая будет выполняться когда происходит событие register_event("BombDrop","DropBomb","abe")
public DropBomb(){
//Считываем четвертый аргумент ( он и говорит о типе события) new type = read_data(4) client_print(0,print_chat,"Bomb drop, type:%d",type) //Простое условие if(type == 0){ client_print(0,print_chat,"Bomb drop") }else if(type == 1){ client_print(0,print_chat,"Bomb planted") } Описание: В принципе описывать тут особо нечего, если вы знакомы с функцией register_event. Хотел бы только обратить внимание на то, что в данном варианте нету id игрока заложившего или уронившего бомбу, для получения этих данных придется воспользоваться другим методом. Имен
Отлов события нового раунда и старта раунда
Отлов события нового раунда и старта раунда Отлов событий Недавно заметил, что не все понимают очевидную разницу между стартом раунда и началом нового раунда. А это принципиально разные вещи. У многих админов на серверах выключен mp_freezetime ( квар поставлен в 0) и по этому событие нового раунда и старта раунда сливается в одну секунду и разница не заметна. Но вы попробуйте поставить этому квару значение скажем в 10 секунд и выполните какую то команду при старте раунда, и сразу заметите очевидную разницу. Вот один из примеров: В моде Predaotor, хищник может двигаться сразу после события начала нового раунда, а все игроки только с момента старта раунда. Правда в данном моде допущена еще более серьезная ошибка с этим моментом, что дает возможность хищнику использовать баг и пополнять запасы жизней и плазмы бесконечное количество раз. Но мы не об этом, вернемся к событиям нового раунда и его старта. Событие нового раунда. Верный способ отловить событие начало нового раунда
register_event("HLTV", "Function", "a", "1=0", "2=0")
Функция "Function" будет вызываться каждый раз когда будет начинаться новый уровень. Ни каких полезных для нас параметров в обработчик не поступает, по этому если вы хотите что либо сделать для какого то конкретного игрока или сразу же всех игроков, в большинстве случаев придется применять циклы. Но хочу обратить внимание, что если в нутрии цикла будет большой или долго выполнимый код, то это может вызвать лаги на сервере. А так же не рекомендую вставлять какое либо логирование в цикл, так как это тоже вызовет ощутимые задержки в работе.
Пример отлова начало нового раунда:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "[register_event] New Round" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR) register_event("HLTV", "NewRound", "a", "1=0", "2=0")
public NewRound(){
client_print(0,print_chat,"Event New Round") Событие старта раунда Я смотрю вы правильно думаете и уже сами хотите мне сказать, что событие старта раунда происходит в момент когда закончиться mp_freezetime. И вы абсолютно правы. Вот только отлавливается это событие немного по другому:
register_logevent("Startround", 2, "1=Round_Start")
Рабочий пример:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "[register_logevent] Round_Start" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {public— StartRound(){DF_Spawn— Функция спавнит объектregister_logevent— Функция отлавливает событияHam_Spawn— Событие респауна игрока.
register_plugin(PLUGIN, VERSION, AUTHOR) register_logevent("StartRound", 2, "1=Round_Start")
client_print(0,print_chat,"Event Start Round") Если вы объедините два этих примера, то вы получите 2 сообщения, одно при событии нового раунда, второе при его старте. Я бы вам рекомендовал это проделать, что бы боле четко уяснить для себя эту принципиальную разницу. Пара слов о событии ResetHUD. Это событие не старта раунда или спавна игрока, как многие считают, а событие которое вызывается при обресечивании HUD экрана игрока. Например при начале записи демки. Можете проверить это на серверах с предатор модом, когда станете хищником и истратите свои 3 плазмы, просто начните записывать и остановите запись демки и у вас пополняться жизни и количество плазм, так как автор мода допустил ошибку с выбором момента когда нужно обновлять количество жизней и плазм у игроков. Надеюсь вы теперь понимаете разницу между этими событиями и научились их отлавливать. Отлов события нового раунда и старта раунда Первый раунд
Ограничение использования команд,функций по раундам
Полезные функции
Пример вычисления кол-ва минут и секунд
Полезные функции Amx Mod X Плагин служит примером вычисления кол-ва минут и секунд из одних только секунд.
#include <amxmodx>
#pragma semicolon 1
#define SECONDS_IN_MINUTE 60
public— plugin_init() {public— CmdSay(const id) {GetEntityFloorDistance— Функция получает расстояние от определенного объекта до пола
register_clcmd("say", "CmdSay");
new szArgs[20]; read_argv(1, szArgs, charsmax(szArgs)); if(equal(szArgs, "/sec", 4)) { // cmd syntax: say /sec <num> new szArg1[4], iMin, iSec; parse(szArgs, szArg1, charsmax(szArg1), szArgs, charsmax(szArgs)); get_minutes(str_to_num(szArgs), iMin, iSec); client_print(id, print_chat, "%d mim %d s", iMin, iSec); // output result in chat return PLUGIN_HANDLED; } return PLUGIN_CONTINUE; get_minutes(const in_sec, &out_min, &out_sec) { out_min = in_sec / SECONDS_IN_MINUTE; out_sec = in_sec % SECONDS_IN_MINUTE;
Полезные функции Amx Mod X Функция получает индекс объекта, на которого наведен прицел.
Синтаксис:
GetEntityFloorDistance(const nEntIndex) [quote][/quote]
nEntIndex— — индекс объекта, от которого будем определять расстояние до пола.is_user_steam— Функция проверяет Steam-игрок или нетstock— bool:is_user_steam(id)
Примечание: если объект на полу, то функция будет возвращать 0.
Полезные функции Amx Mod X Многие согласны с мнением что игроков с лицензионным клиентом необходимо поощрять, что бы 100% определить со стима игрок или нет поможет функция is_user_steam ( для серверов с установленным dp_roto)
// Author Sh0oter
static dp_pointer if(dp_pointer || (dp_pointer = get_cvar_pointer("dp_r_id_provider"))) { server_cmd("dp_clientinfo %d", id) server_exec() return (get_pcvar_num(dp_pointer) == 2) ? true : false } return false Новость отредактировал Safety1st — 12-08-2014, 02:05
is_hull_vacant— Функция проверяет возможность перемещения игрока в заданную точку.ham_give_weapon— Функция выдает определенное оружие игроку с помощью weapon_ константstock— ham_give_weapon(id,weapon[])ham_strip_weapon— Функция отбирает определенное оружие у игрока
Полезные функции Amx Mod X Актуальность данной функции заключается в том, что если не выполнить проверку чем является точка куда перемещаем игрока, то он может оказаться в стене, полу или в другом игроке, что не даст возможность продолжать играть. Функция is_hull_vacant вернет результат проверки что вокруг проверяемой точки достаточно места для того что бы туда переместить игрока. Функция изменяет количество патронов в магазине. Полезные функции Amx Mod X Достаточно часто встречается вопрос "как выдать патроны", но особая сложность возникает при выдачи их в магазин(обойму), не говоря уже о том, что используют зачастую целый модуль ради одной функции. Лично я предпочитаю писать палгины на fakemeta и hamsandwich так как они способны заменить сразу несколько других модулей.
Полезные функции Amx Mod X Из заголовка уже понятно что делает функция и думаю что нет необходимости в пояснении важности момента выдачи оружия игроку. Исходный код функции:
if(!equal(weapon,"weapon_",7)) return 0 new wEnt = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,weapon)); if(!pev_valid(wEnt)) return 0 set_pev(wEnt,pev_spawnflags,SF_NORESPAWN); dllfunc(DLLFunc_Spawn,wEnt) if(!ExecuteHamB(Ham_AddPlayerItem,id,wEnt)) { if(pev_valid(wEnt)) set_pev(wEnt,pev_flags,pev(wEnt,pev_flags) | FL_KILLME); return 0 } ExecuteHamB(Ham_Item_AttachToPlayer,wEnt,id) return 1
Полезные функции Amx Mod X В Counter-Strike оружие игрока — один из важнейших моментов, по этому с этим предметом работать приходится часто, а работа в основном связана с тем что бы удалить или выдать оружие. Функция ham_strip_weapon позволяет удалить конкретное оружие у игрока. Исходный код функции:
#include <fakemeta>
#include <hamsandwich>
stock ham_strip_weapon(id,weapon[])
if(!equal(weapon,"weapon_",7)) return 0 new wId = get_weaponid(weapon) if(!wId) return 0 new wEnt while((wEnt = engfunc(EngFunc_FindEntityByString,wEnt,"classname",weapon)) && pev(wEnt,pev_owner) != id) {} if(!wEnt) return 0 if(get_user_weapon(id) == wId) ExecuteHamB(Ham_Weapon_RetireWeapon,wEnt) if(!ExecuteHamB(Ham_RemovePlayerItem,id,wEnt)) return 0 ExecuteHamB(Ham_Item_Kill,wEnt); set_pev(id,pev_weapons,pev(id,pev_weapons) & ~(1<<wId)) return 1
is_user_steam Функция проверяет Steam-игрок или нет
is_user_steam— Функция проверяет Steam-игрок или нетstock— bool:is_user_steam(id)
Полезные функции Amx Mod X Многие согласны с мнением что игроков с лицензионным клиентом необходимо поощрять, что бы 100% определить со стима игрок или нет поможет функция is_user_steam ( для серверов с установленным dproto)
// Author Sh0oter
static dp_pointer if(dp_pointer || (dp_pointer = get_cvar_pointer("dp_r_id_provider"))) { server_cmd("dp_clientinfo %d", id) server_exec() return (get_pcvar_num(dp_pointer) == 2) ? true : false } return false
Синтаксис:
is_user_steam(id)
id— — Индекс проверяемого игрока
Функция вернет false или true ( игрок не стим или стим) Тип функции: stock
Пример:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#include <cstrike>
#define PLUGIN "[ is_user_steam" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {public— client_connect(id){stock— bool:is_user_steam(id)
register_plugin(PLUGIN, VERSION, AUTHOR)
server_print("Client steam? [%s]",is_user_steam(id) ? "Yes":"No")
// Author Sh0oter
static dp_pointer if(dp_pointer || (dp_pointer = get_cvar_pointer("dp_r_id_provider"))) { server_cmd("dp_clientinfo %d", id) server_exec() return (get_pcvar_num(dp_pointer) == 2) ? true : false } return false Описание: В результате работы данного примере, в консоль сервера будет выводиться сообщение о том стим игрок или нет, при каждом коннекте к серверу. Пока игрок на сервере — его 'тип' измениться не может. Поэтому функцию is_user_steam() для игрока достаточно вызвать однократно и запомнить результат. Новость отредакт
Функция выдает патроны, забирает патроны или устанавливает необходимое количество патронов в магазине
Функция изменяет количество патронов в магазине. Полезные функции Amx Mod X Достаточно часто встречается вопрос "как выдать патроны", но особая сложность возникает при выдачи их в магазин(обойму), не говоря уже о том, что используют зачастую целый модуль ради одной функции. Лично я предпочитаю писать палгины на fakemeta и hamsandwich так как они способны заменить сразу несколько других модулей. set_pdata_int
get_pdata_cbase— -Получает данные от блока памяти, в данном случае это Offset_active_item, активное оружие игрока.
Синтаксис:
set_pdata_int(get_pdata_cbase(id, 373), 51, AMMO) get_pdata_cbase(id, 373) — id заменить на индекс игрока ( если он в вашей функции не id) 51 — не трогайте.(OFFSET_CLIPAMMO )
AMMO— — Сколько патронов выдать
Пример:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <fakemeta>
#include <hamsandwich>
#define PLUGIN "[ ammo" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {public— give_ammo(id){
register_plugin(PLUGIN, VERSION, AUTHOR) register_clcmd("say /ammo","give_ammo")
set_pdata_int(get_pdata_cbase(id, 373), 51, 3)//В магазин Описание: В любой момент времени напишите в чат
ham_give_weapon Функция выдает определенное оружие игроку с помощью weapon_ констант
ham_give_weapon— Функция выдает определенное оружие игроку с помощью weapon_ константstock— ham_give_weapon(id,weapon[])
Полезные функции Amx Mod X Из заголовка уже понятно что делает функция и думаю что нет необходимости в пояснении важности момента выдачи оружия игроку. Исходный код функции:
if(!equal(weapon,"weapon_",7)) return 0 new wEnt = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,weapon)); if(!pev_valid(wEnt)) return 0 set_pev(wEnt,pev_spawnflags,SF_NORESPAWN); dllfunc(DLLFunc_Spawn,wEnt) if(!ExecuteHamB(Ham_AddPlayerItem,id,wEnt)) { if(pev_valid(wEnt)) set_pev(wEnt,pev_flags,pev(wEnt,pev_flags) | FL_KILLME); return 0 } ExecuteHamB(Ham_Item_AttachToPlayer,wEnt,id) return 1
Синтаксис:
ham_give_weapon(id,weapon[])
id— — Индекс игрока
weapon[] — Имя выдаваемого оружия (weapon_*) Функция вернет 1 если все ок Тип функции: stock
Пример:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <hamsandwich>
#define PLUGIN "[The useful functions] ham_give_weapon" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {public— give_AK(id){public— give_AWP(id){stock— ham_give_weapon(id,weapon[])say— AK и say AWP обе они выдают оружие игроку, в примере показано как использовать функцию, а данное пояснение написано только к тому что, выполнив поочередно эти две команды у вас будет 2 оружия одновременно, по этому если вы хотите поменять игроку оружие, то при
register_plugin(PLUGIN, VERSION, AUTHOR) register_clcmd("say AWP","give_AWP") register_clcmd("say AK","give_AK")
ham_give_weapon(id,"weapon_ak47")
ham_give_weapon(id,"weapon_awp")
if(!equal(weapon,"weapon_",7)) return 0 new wEnt = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,weapon)); if(!pev_valid(wEnt)) return 0 set_pev(wEnt,pev_spawnflags,SF_NORESPAWN); dllfunc(DLLFunc_Spawn,wEnt) if(!ExecuteHamB(Ham_AddPlayerItem,id,wEnt)) { if(pev_valid(wEnt)) set_pev(wEnt,pev_flags,pev(wEnt,pev_flags) | FL_KILLME); return 0 } ExecuteHamB(Ham_Item_AttachToPlayer,wEnt,id) return 1 Описание: И так есть 2 команды:
ham_strip_weapon Функция отбирает определенное оружие у игрока
ham_strip_weapon— Функция отбирает определенное оружие у игрока
Полезные функции Amx Mod X В Counter-Strike оружие игрока — один из важнейших моментов, по этому с этим предметом работать приходится часто, а работа в основном связана с тем что бы удалить или выдать оружие. Функция ham_strip_weapon позволяет удалить конкретное оружие у игрока. Исходный код функции:
#include <fakemeta>
#include <hamsandwich>
stock ham_strip_weapon(id,weapon[])
if(!equal(weapon,"weapon_",7)) return 0 new wId = get_weaponid(weapon) if(!wId) return 0 new wEnt while((wEnt = engfunc(EngFunc_FindEntityByString,wEnt,"classname",weapon)) && pev(wEnt,pev_owner) != id) {} if(!wEnt) return 0 if(get_user_weapon(id) == wId) ExecuteHamB(Ham_Weapon_RetireWeapon,wEnt) if(!ExecuteHamB(Ham_RemovePlayerItem,id,wEnt)) return 0 ExecuteHamB(Ham_Item_Kill,wEnt); set_pev(id,pev_weapons,pev(id,pev_weapons) & ~(1<<wId)) return 1
Синтаксис:
ham_strip_weapon(id,weapon[])
id— — Индекс игрока
weapon[] — Название оружия weapon_* Функция вернет 1 если оружие удалено. Тип функции: stock
Пример:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <hamsandwich>
#define PLUGIN "[The useful functions] ham_strip_weapon" #define VERSION "1.0" #define AUTHOR "Admin"
public— plugin_init() {public— un_weapon(id){stock— ham_strip_weapon(id,weapon[])
register_plugin(PLUGIN, VERSION, AUTHOR) register_clcmd("say strip","un_weapon")
//Получение id оружия new pl_weapon = get_user_weapon(id) new weapon_name[24] //Получение названия оружия get_weaponname(pl_weapon,weapon_name,24) //Вызов функции ham_strip_weapon(id,weapon_name)
if(!equal(weapon,"weapon_",7)) return 0 new wId = get_weaponid(weapon) if(!wId) return 0 new wEnt while((wEnt = engfunc(EngFunc_FindEntityByString,wEnt,"classname",weapon)) && pev(wEnt,pev_owner) != id) {} if(!wEnt) return 0 if(get_user_weapon(id) == wId) ExecuteHamB(Ham_Weapon_RetireWeapon,wEnt) if(!ExecuteHamB(Ham_RemovePlayerItem,id,wEnt)) return 0 ExecuteHamB(Ham_Item_Kill,wEnt); set_pev(id,pev_weapons,pev(id,pev_weapons) & ~(1<<wId)) return 1 Описание: В данном примере скрипт получает какое оружие у игрока, выполнившего команду say strip, и передает его индекс и название его оружие функции ham_strip_weapon, которая в свою очередь удаляет о
StopSound Функция останавливает проигрываемые звуки у игрока
StopSound— Функция останавливает проигрываемые звуки у игрокаstock— StopSound(pId, iType = STOP_SOUND_ALL)
Полезные функции Amx Mod X За последнее время несколько раз слышал вопрос о том как заблокировать или остановить звуки, ответ нашелся на форуме, что бы функция не затерялась выкладываю ее сдесь. Исходный код функции: #define STOP_SOUND_WAV 0 #define STOP_SOUND_MP3 1 #define STOP_SOUND_ALL 2
switch (iType) { case STOP_SOUND_WAV: client_cmd(pId, "stopsound"); case STOP_SOUND_MP3: client_cmd(pId, "mp3 stop"); case STOP_SOUND_ALL: client_cmd(pId, "stopsound;mp3 stop"); } return 1; }Автор:PRoSToTeM@
Синтаксис:
StopSound(pId, iType = STOP_SOUND_ALL) pId — id игрока
iType— = STOP_SOUND_ALL — какой тип звуков останавливать:#define STOP_SOUND_WAV 0
#define STOP_SOUND_MP3 1 #define STOP_SOUND_ALL 2 Тип функции: stock
Пример:
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "[stock function] Stop" #define VERSION "1.0" #define AUTHOR "Admin" #define STOP_SOUND_WAV 0 #define STOP_SOUND_MP3 1 #define STOP_SOUND_ALL 2
public— plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR) //Все три события вызывают одну функцию stop_sd register_event("SendAudio", "stop_sd", "a", "2&%!MRAD_terwin") register_event("SendAudio", "stop_sd", "a", "2&%!MRAD_ctwin") register_event("SendAudio", "stop_sd", "a", "2&%!MRAD_rounddraw")
public stop_sd(){
new Players[32] new playerCount, i, id get_players(Players, playerCount, "ch") for (i=0; i<playerCount; i++) { id = Players[i] //Вызов функции блокировки звука StopSound(id,2) }
stock— StopSound(pId, iType = STOP_SOUND_ALL)
switch (iType) { case STOP_SOUND_WAV: client_cmd(pId, "stopsound"); case STOP_SOUND_MP3: client_cmd(pId, "mp3 stop"); case STOP_SOUND_ALL: client_cmd(pId, "stopsound;mp3 stop"); } return 1; Описание: Приведенный пример блокирует звуки при победе контров, терроров и ничьей. Не забывайте что в отлове этих событий нет id игроков и по этому приходится применят
Weapon Menu Hardcoded
Weapon— Menu Hardcoded
Готовые скрипты Легко расширяемое простейшее меню оружий, доступное с определённого раунда. Только одно оружие и только 1 раз за раунд. Версия: 0.6b от 26.08.2014 Скачать: официальный топик Меню открывается по команде weapon_menu. Если не дошли до 'нужного' раунда либо уже брали — пункты недоступны, если нет прав на использование или игрок мёртв — тоже. Последней опцией в меню идёт обмундирование: броня и полный пакет гранат. Плагин корректно работает в случае наличия щита у игрока. Настройка — в исходнике: • #define ACCESS_FLAG ADMIN_LEVEL_H — флаг доступа, по умолчанию флаг t ; • const giAllowedRound = 3 — с какого раунда меню будет доступно; • //#define CHECK_SHIELD — раскомментируйте на серверах, где доступна покупка щита; • #define ITEMS_COUNT 4 — число оружий в меню, максимум: 8, если активен пункт 'Equipment', иначе — 9; • new const ITEMS — таблица с параметрами. Число строк должно точно совпадать с ITEMS_COUNT выше. Формат таблицы: название пункта меню, id выдаваемого оружия, число патронов, выдаваемых в запас. Id всех оружий можно посмотреть в файле amxconst.inc ; • #define EQUIPMENT_ITEM "Equipment" — название в меню, закомментируйте для отключения пункта. Дополнительная информация • Не хотелось делать очередной говноплагин, коих нава
Record Demo Question
Record— Demo QuestionRecord— Demo Questionmenu_addblank— Функция добавляет пустую строку в менюmenu_display— Функция показывает меню игрокуmenu_additem— Функция добавляет пункт в меню
Готовые скрипты Плагин просит разрешения на запись демо. Если игрок не согласится, его кикнет с сервера. Я постарался сделать плагин по-настоящему удобным и универсальным. Нестандартные кнопки. Меню всегда появляется в удачный момент. У игроков много демок не накапливается. Авторы: Serjant и Safety1st Версия: 0.3 от 18.08.2014 Скачать: официальный топик Основные недостатки оригинальной версии: • хитрецы перекрывают меню рекордера другим и таким образом избегают выбора в меню; • может произойти ситуация, что меню рекордера перекрывает меню выбора класса (скина). т.о. игрок остаётся без класса, возрождается с помощью VIP-меню и творит на сервере 'чудеса', оставаясь невидимым; • у игроков скапливается куча демок. Изменения — добрейшие: • меню плагина не перекрывает никакие другие меню. если игроку меню в данный момент показать нельзя – оно будет показано позже; • меню показывается игроку сразу после выбора класса, но только если тот не заспавнился. для отключения закомментировать строку с SHOW_AFTER_CLASS_CHOOSING ; • если меню не было показано при выборе класса, оно показывается игроку после смерти. для отключения закомментировать строку с SHOW_AFTER_DEATH ; • если обе предыдущие опции отключены, плагин периодически пытается показать меню новому игроку (см. ниже); • игрок, не сделавший выбор в меню за отведённое время (см. ниже), удаляется с сервера; • в настройках можно задать, какие именно кнопки использовать в меню: строки с BUTTON_OK и BUTTON_CANCEL ; • демки именуются в формате 'префикс_название_карты'; • упразднены cvar'ы. • DEMO_PREFIX — префикс в названии демки; • gflMenuDelay — сколько времени даётся игроку на выбор, а также интервал повторных попыток показа меню. '.0' на конце обязателен! • THEONLY_DEMO — раскомментируйте, чтобы название карты не входило в состав названия демки. тогда новая демка будет всегда перезаписывать старую; • игроки с флагом IMMUNITY_FLAG освобождены от обязательной записи демки, по дефолту это флаг 'a'. чтобы отключить 'иммунитет' — закомментируйте строку с IMMUNITY_FLAG. Несколько флагов для иммунитета указываются так: #define IMMUNITY_FLAG ( ADMIN_IMMUNITY | ADMIN_BAN ) • меню и сообщение при кике настраиваются в словаре. Скрин Дополнительная информация Дефолтные опции – SHOW_AFTER_CLASS_CHOOSING и SHOW_AFTER_DEATH – рекомендуемые. С ними меню будет показываться только мёртвым игрокам: сразу после выбора класса или сразу после смерти (смотря что наступит раньше ;). Это подойдёт для большинства серверов. Но на некоторых одна или обе из них будут лишними и/или приведут к проблемам. Например, на CSDM нужно выключить SHOW_AFTER_DEATH : там респавн игроков, меню рекордера, показанное после смерти, может быть перекрыто меню /guns при спавне, и игрока в итоге кикнет. На серверах JailBreak, KZ и прочих, где игроки принудительно загоняются в одну из команд, наоборот, не нужна и будет лишней опция SHOW_AFTER_CLASS_CHOOSING : игроки просто не доходят до выбора класса. Когда опция SHOW_AFTER_DEATH выключена, но SHOW_AFTER_CLASS_CHOOSING – включена и игрок после выбора класса заспавнился, будут выполняться попытки показать ему меню с интервалом gflMenuDelay в независимости от того, мёртв он или нет. В общем, если есть какие-то сомнения, что всё будет работать корректно c конкретными настройками, нужно самому проверить все варианты на сервере.
Мультиязычное меню Новая система меню AMXX и все ее функции.
Roomtype Reset
Roomtype — Reset Готовые скрипты На картах as_tundra, cs_docks, cs_siege, de_luxor и некоторых других есть зоны, где звуки приобретают несколько иное звучание (появляется 'эхо'). Из-за недоработки в клиенте после смены карты у игрока звуки с эффектами могут остаться; лечится это дело рестартом игры или установкой квара room_type в 0. Плагин — обход проблемы: без слоухакинга заставляет зашедших игроков сбросить квар на значение по умолчанию. Плагин должен быть прописан непосредственно в plugins.ini : когда фикс не нужен — он и не применяется; позиция не важна. Версия: 0.5 от 17.08.2014 Скачать: официальный топик Новость отредактировал Safety1st — 22-08-2014, 10:06Причина: v0.5 Roomtype ResetRecord— Demo QuestionStopSound— Функция останавливает проигрываемые звуки у игрокаcvar_exists— Функция проверяет существовование квараregister_cvar— Функция регистрирует новый квар.
Релиз плагина — Chat_Info
Изменение отдачи оружия
Изменение отдачи оружия Готовые скрипты
Как пользоваться ?
set_user_recoil(const Player,Float:recoil) stock функция принимает два параметра,const Player - id игрока у которого будем менять отдачу и Float:recoil - дробное число отвечающее за повышение/уменьшение отдачи
Пример кода:
#include <amxmodx>
#include <amxmisc>
#include <recoil>
#include <Hamsandwich>
#include <fakemeta>
new bool:user_enabled_norecoil[33] = false
public— plugin_init() {public— no_recoil_func(weapon)
register_plugin(PLUGIN, VERSION, AUTHOR) register_clcmd("say !admin_norecoil_on", "nron") register_clcmd("say !admin_norecoil_off", "nroff") RegisterHam(Ham_Weapon_PrimaryAttack, "weapon_ak47", "no_recoil_func", 1)
new id = pev(weapon,pev_owner) if( user_enabled_norecoil[id] == true ) { set_user_recoil(id,0.0) }
public nron(id)
if( get_user_flags(id) & ADMIN_BAN ) { user_enabled_norecoil[id] = true set_hudmessage(255, 255, 0, 0.0, 0.9, 0, 6.0, 12.0) show_hudmessage(id, "[Admin No-Recoil Enabled]") }
public nroff(id)
if( get_user_flags(id) & ADMIN_BAN ) { user_enabled_norecoil[id] = false set_hudmessage(255, 255, 0, 0.0, 0.9, 0, 6.0, 12.0) show_hudmessage(id, "[Admin No-Recoil Disabled]") } Разбор кода по частям: 1. #include <recoil> как вы уже заметили я уже включил нужную нам библиотеку,думаю #include не нуждается в объяснении и вы уже прочли все уроки по скриптингу(на этом сайте) 2. RegisterHam(Ham_Weapon_PrimaryAttack, "weapon_ak47", "no_recoil_func", 1) — регистрируем хам который будет выполнять no_recoil_func функцию при стрельбе с ak47 3. new id = pev(weapon,pev_owner) — узнаём кто хозяин оружия и записываем в переменную id 4. set_user_recoil(id,0.0) устанавливаем отдачу у игрока при стрельбе с отлавливаемого оружия Остальная часть кода надеюсь вам ясна и не нуждается в обьяснении Ссылка на скачивание Я не жадны
Функция get_user_fps [FPS Api]
Функция get_user_fps [FPS Api] Готовые скрипты Авторы: .csF Dev Team Версия: 1.2 Описание: С помощью этого плагина вы сможете измерить количество FPS игрока, я максимально уменьшил количество ложных показаний(их фактически нет). Использовать просто: 1. Скачать исходник плагина get_user_fps. 2. Скомпилировать его. 3. Закинуть скомпилированый плагин в папку plugins у вас на сервере. 4. Прописать плагин в plugins.ini(желательно в самом верху). 5. Закинуть инклюд fps.inc в папку include. 6. Дописать в своём плагине строчку #include . 7. И в вашем плагине, где нужно узнать fps игрока просто пишем get_user_fps(id) . Используемые модули: Список изменений: 1.0b — Релиз 1.0 — get_maxplayers() — в глобальную, убрана проверка на коннект(стормозил) //Спасибо TurangaLeela 1.1 — Забил выделенную память //Спасибо TurangaLeela, переместил корректировку fps из натива.
/* Отдельное спасибо TurangaLeela с amx-x.ru */
#include <amxmodx>
#include <engine>
#define A "FPS" #define B "1.2" #define C ".csF Dev Team" #define TASK_ID 55055 #define g_Maxplayers Fps[0][CONST] enum _:FPS_DATA COUNT, CONST
};
new Fps[33][FPS_DATA];
public plugin_init()
register_plugin(A, B, C); g_Maxplayers = get_maxplayers(); set_task(1.0, "Clear", TASK_ID, "", 0, "b");
public— plugin_natives()public— native_get_user_fps(id)public— client_PreThink(id)
register_native("get_user_fps", "native_get_user_fps", 1);
return Fps[id][CONST];
Fps[id][COUNT]++;
public Clear()
for(new id = 1; id <= g_Maxplayers; id++) { Fps[id][CONST] = Fps[id][COUNT]+=3; Fps[id][COUNT] = 0; }
public— client_connect(id)public— client_disconnect(id)
Fps[id][COUNT] = 0;
Fps[id][COUNT] = 0; Использование API в своём плагине или как я использую FPS Api:
#include <amxmodx>
#include <amxmisc>
#include <fps> //Подключаем fps.inc в свой плагин
#define A "FPS Controler" #define B "1.2" #define C ".csF Group" #define TASK 9999
new— max_fps; //Создаём 2 глобальные переменные
new task_time;
public plugin_init()
register_plugin(A, B, C); register_cvar("csf_fps_control", "60"); //Регистрируем квар "csf_fps_control", он будет отвечать за время, через которое проверит фпс у игрока, после входа на сервер register_cvar("csf_fps_max", "101"); //Регистрируем квар "csf_fps_max", он будет отвечать за максимальное количество fps у игрока
public— plugin_cfg()public— client_putinserver(id)public— client_disconnect(id)public— FpsControl(id)
task_time = get_cvar_float("csf_fps_control"); //Записываем в глобальную переменную число квара "csf_fps_control" fps_max = get_cvar_num("csf_fps_max"); //Аналогично с "csf_fps_max"
set_task(task_time, "FpsControl", id + TASK); //Запускаем задание на функцию "FpsControl" через время, указанное в глобальной переменной "task_time" и с id игрока, к которому прибавили 9999
remove_task(id + TASK); //Удаляем задание, если игрок выходит с сервера
id -= TASK; //Вычитаем из id 9999, чтобы получить id игрока new fps = get_user_fps(id); //Создаём локальную переменную и в неё записываем значение fps игрока if(fps > max_fps) //Делаем проверку, если количество fps игрока больше, чем указанно в глобальной переменной "max_fps", то { new userid = get_user_userid(id); //Создаём локальную переменную, записав в неё userid игрока new name[32]; //Создаём массив, в него мы впишем ник игрока get_user_name(id, name, charsmax(name)); //Вписываем ник игрока client_print(0, print_chat, "Количество FPS игрока %s превысило ограничения сервера, FPS: %d!", name, fps); //Выводим в чат инфо
Делаем приставку в чате
Делаем приставку в чате Готовые скрипты В этой статье опишу как сделать приставку в чате для админов и випов на ваше усмотрение… Ну начнём пожалуй Подключаем нужные нам инклюды
#include <amxmodx>
#include <amxmisc>
#include <cstrike>
Они нам понадобятся в дальнейшем регистрируем плагин название автора версию
#include <amxmodx>
#include <amxmisc>
#include <cstrike>
#define PLGUIN "Message mod for #define AUTHOR "Nesquik" Вписать ваше имя )) #define VERSION "1.0"
public— plugin_init()
register_plugin(PLUGIN,AUTHOR,VERSION)
register_cvar("admin_acces","m")
register_message(get_user_msgid("SayText"),"Tekst") // Отлавливаем событие написания игроком сообщения
//Теперь перейдём к созданию нашего сообщения
public Tekst(msgId,msgDest,msgEnt)
new id = get_msg_arg_int(1) // сокращаем ид игрока который пишет сообщения
Отлавливаем подключен ли игрок к серверу или нет if(!is_user_connected(id))
return— PLUGIN_CONTINUE;return— PLUGIN_CONTINUE // Продолжаем работу плагинаpublic— admin_acces() // Эта функция получает флаг для определенного доступа в
if(get_user_flags(id) & admin_acces()) // Проверяем на наличие нужных нам флагов new szTmp[256],szTmp2[256]; get_msg_arg_string(2,szTmp, charsmax( szTmp ) ) // поулчаем написаное сообщение new szPrefix[64] = "^x04[Админ]"; // Наш префикс if(!equal(szTmp,"#Cstrike_Chat_All")) // Если чат между коммандой { add(szTmp2,charsmax(szTmp2),szPrefix); // Ставим наш префикс add(szTmp2,charsmax(szTmp2)," "); add(szTmp2,charsmax(szTmp2),szTmp); // Сообщение }else{ // Если чат общий add(szTmp2,charsmax(szTmp2),szPrefix); //наш префикс add(szTmp2,charsmax(szTmp2)," ^x03%s1^x01: ^x04%s2") // Имя игрока и сообщение } set_msg_arg_string(2,szTmp2); //Устанавливаем вид который мы сделали выше
Релиз плагина — Damage Killer
Релиз плагина — Damage Killer Готовые скрипты Версия: 1.0 специально для Плагин: пишет игрокам цветные сообщения в чат с именем, количеством здоровья и брони его убийцы, а также сколько здоровья он отнял у Вас и Вы у него. Команда для игроков — dk — включить/отключить, в общий или командный чат, со слешем или без. Значение команды сохраняется в конфиге игрока — setinfo dk on/off. Есть квар: dk_delay — через сколько секунд после смерти выводить сообщение, чтобы учитывался урон от гранаты взорвавшейся после смерти, по умолчанию 2 секунды. Для работы необходимы плагин colorchat и инклуд chatcolor.
#include<amxmodx>
#include<chatcolor>
new g_DK_Damage[32][32]
public plugin_init()
register_plugin("[ Damage Killer", "1.0", "Valer4") register_clcmd("say /dk", "dk_switch") register_clcmd("say dk", "dk_switch") register_cvar("dk_delay", "2.0")
public— client_damage(b_DK_Attacker_A, b_DK_Victim_A, b_DK_Damage)public— client_death(b_DK_Attacker_B, b_DK_Victim_B)public— dk_reset(b_DK_PID_A)public— dk_switch(b_DK_PID_B)
g_DK_Damage[b_DK_Attacker_A][b_DK_Victim_A] += b_DK_Damage
if(b_DK_Attacker_B != b_DK_Victim_B) { new DK_Killer_Name[32], DK_Damage_Killer_A[32] get_user_info(b_DK_Victim_B, "damage_killer", DK_Damage_Killer_A, 31) if(!equali(DK_Damage_Killer_A, "off")) { get_user_name(b_DK_Attacker_B, DK_Killer_Name, 31) client_print_color(b_DK_Victim_B, Red, "^4Вас убил ^3%s^4, у него осталось ^3%d ^4HP и ^3%d ^4AP", DK_Killer_Name, get_user_health(b_DK_Attacker_B), get_user_armor(b_DK_Attacker_B)) client_print_color(b_DK_Victim_B, Red, "^4Он отнял у Вас ^3%d ^4HP, а Вы у него ^3%d ^4HP", g_DK_Damage[b_DK_Attacker_B][b_DK_Victim_B], g_DK_Damage[b_DK_Victim_B][b_DK_Attacker_B]) } } set_task(get_cvar_float("dk_delay"), "dk_reset", b_DK_Victim_B)
new DK_Players[32], DK_Num, DK_Item get_players(DK_Players, DK_Num) for(DK_Item = 0; DK_Item < DK_Num; DK_Item ++) { g_DK_Damage[DK_Players[DK_Item]][b_DK_PID_A] = 0 g_DK_Damage[b_DK_PID_A][DK_Players[DK_Item]] = 0 }
new DK_Damage_Killer_B[32] get_user_info(b_DK_PID_B, "damage_killer", DK_Damage_Killer_B, 31) if(equali(DK_Damage_Killer_B, "off")) { client_cmd(b_DK_PID_B, "setinfo damage_killer on") client_print_color(b_DK_PID_B, Red, "^4Показ сообщений об уроне — ^3ВКЛЮЧЕН^4!") return PLUGIN_HANDLED } else { client_cmd(b_DK_PID_B, "setinfo damage_killer off") client_print_color(b_DK_PID_B, Red, "^4Показ сообщений об уроне — ^3ОТКЛЮЧЕН^
Релиз плагина — Chat_Info
Релиз плагина — Chat_Info Готовые скрипты Версия: 1.0 специально для Плагин: показывает цветные сообщения с нужной Вам информацией в чат игрокам,через некоторое время после смерти, если игрок ещё мёртв. Для работы необходимы плагин colorchat и инклуд chatcolor. Количество сообщений не ограничено. Квар — ci_delay — через сколько секунд после смерти показывать сообщения, по умолчанию 13. Команда для игроков — ci — включить/отключить показ сообщений, в общий или командный чат, со слешем или без. Её значение сохраняется в конфиге у игроков — setinfo chat_info ci_on/ci_off. В папку cstrikeaddonsamxmodxdatalang добавляем файл chat_info.txt и записываем в него: [ru]
CI_ON— = Показ сообщений об особенностях сервера — Включен!CI_OFF— = Показ сообщений об особенностях сервера — Отключен!TEXT_1— = Ваше !gсообщение !t1TEXT_2— = Ваше !gсообщение !t2TEXT_3— = Ваше !gсообщение !t3TEXT_4— = Ваше !gсообщение !t4TEXT_5— = Ваше !gсообщение !t5CI_ON— = Показ сообщений об особенностях сервера — Включен!CI_OFF— = Показ сообщений об особенностях сервера — Отключен!TEXT_1— = Ваше !gсообщение !t1TEXT_2— = Ваше !gсообщение !t2TEXT_3— = Ваше !gсообщение !t3TEXT_4— = Ваше !gсообщение !t4TEXT_5— = Ваше !gсообщение !t5
[en]
Заменяем "Ваше сообщение N" на нужное и сохраняем. Если ваше сообщение на русском, Вам понадобиться текстовый редактор(например AkelPad) позволяющий сохранять в кодировке 65001(UTF-8) без галочки BOM. Доступные цвета: красный по умолчанию и зелёный. Исходный код плагина:
#include<amxmodx>
#include<chatcolor>
new g_CI_Deaths[32]
new c_CI_Delay
public plugin_init()
register_plugin("[ Chat_Info", "1.0", "Valer4") c_CI_Delay = register_cvar("ci_delay", "13.0") register_clcmd("say ci", "ci_switch") register_clcmd("say /ci", "ci_switch") register_clcmd("say_team ci", "ci_switch") register_clcmd("say_team /ci", "ci_switch") register_dictionary("chat_info.txt")
public— client_death(CI_Not_Used, CI_User_Dead_A)public— ci_message(CI_User_Dead_B)
new CI_Chat_Info_A[32] get_user_info(CI_User_Dead_A, "chat_info", CI_Chat_Info_A, 31) if(!equali(CI_Chat_Info_A, "ci_off")) { set_task(get_pcvar_float(c_CI_Delay), "ci_message", CI_User_Dead_A) }
if(!is_user_alive(CI_User_Dead_B)) { new CI_Data[192], CI_Data_B[192] g_CI_Deaths[CI_User_Dead_B] ++ format(CI_Data, 191, "TEXT_%d", g_CI_Deaths[CI_User_Dead_B]) format(CI_Data_B, 191, "%L", CI_User_Dead_B, CI_Data) if(!equali(CI_Data_B, "ML_NOTFOUND: TEXT_%d", g_CI_Deaths[CI_User_Dead_B])) { client_print_color(CI_Use
Скрипт скрывает деньги у игрока.
Скрипт скрывает деньги у игрока. Готовые скрипты Не редко спрашивают как скрыть деньги у игрока, ответ на этот вопрос я подсмотрел в плагине GunGame, обратите внимание что в данном примере деньги скрываются по команде, то есть при следующем событии ResetHUD они вновь появятся и их вновь потребуется убрать.
Пример:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "[no money] No money" #define VERSION "1.0" #define AUTHOR "Avalanche & Admin"
public— plugin_init() {public— hide_money(id){
register_plugin(PLUGIN, VERSION, AUTHOR) register_clcmd("say nomoney","hide_money")
// hide money message_begin(MSG_ONE,94,_,id); write_byte(1<<5); message_end(); // hide crosshair that appears from hiding money message_begin(MSG_ONE,110,_,id); write_byte(0); message_end(); Вот так денег не будет постоянно:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "[no money] No money" #define VERSION "1.0" #define AUTHOR "Avalanche & Admin"
public— plugin_init() {public— event_resethud(id){public— hide_money(id){write_string— Функция изменяет одну строкуwrite_byte— Функция изменяе
register_plugin(PLUGIN, VERSION, AUTHOR) register_event("ResetHUD","event_resethud","be");
if(is_user_connected(id)) set_task(0.1,"hide_money",id)
if(is_user_connected(id)){ // hide money message_begin(MSG_ONE,94,_,id) write_byte(1<<5) message_end() // hide crosshair that appears from hiding money message_begin(MSG_ONE,110,_,id) write_byte(0) message_end() } Скрипт скрывает деньги у игрока.
Плагин для создания ботов
Плагин для создания ботов Готовые скрипты Плагин для создания ботов, которых использую для тестирования функций. Это не совсем обычные боты, у них есть 4 варианта поведения: 1 Ничего не делать 2 Копирование движений создателя бота 3 Тоже самое что и 2 + стрельба 4 Противоположные действия Так же я закоментировал строки, которые отвечают за то, что бы нельзя было создать более одного бота одним игроком. Хотя у меня есть второй компьютер, и могу всегда проверить функцию где нужны именно 2 игрока, зачастую бывает проще все проверить с ботом.
/************************************************************************************************
* Potti v1.40, by p3tsin
*************************************************************************************************
* Description:
* Allows admins to create a bot which can be fully controlled,
* e.g. move it and handle its menus
* Commands:
* amx_botadd <name> - create a new bot
* amx_botexec <cmd> - execute a command on your bot
* amx_botmove <0-3> - 0: off
* 1: copy your movements
* 2: same as #1 + aim the same spot when shooting
* 3: look the opposite way and do the opposite moves
* amx_botcmds <1/0> - when enabled, all owner commands are sent to his/her bot
* amx_botdel - kick your bot off the server
* Server cvars:
* potti_hudcolor <r g b> - changes the color of the hud message
* potti_hudpos <x y> - changes the position of the hud message
* Note:
* When executing commands, only engine cmds will work, so for example cvars cannot
* be changed. Also, I recommend you bind a button to "messagemode amx_botexec",
* it will be more comfortable to use than opening console every time :)
* To Do:
* - nothing really (don't feel like messing with buyzone)
* Known bugs:
* - (cs) when owner is dead, pressing duck will open the specmenu which is really annoying :(
* - (cs) can't open buymenu via fullcontrol mode if not in buyzone (or dead)
* Changelog:
* 1.00 - Initial release
* 1.10 - Added full control mode
* - Shows money left on a bot when a buymenu is opened
* 1.20 - Modified so everyone (with access) can have their own bot
* - Cstrike-module is no more needed to display money
* - After dying, the owner will automatically move to spectating to his/her bot if botmove is on
* - Messages printed to a bot (to center and chat) are shown to its owner too
* - Fixed bot latency to show as "BOT"
* - More efficient msec calculations
* - Other little improvements
* 1.30 - Added support for The Specialists (press 6 to switch to kungfu when in fullcontrol mode)
* - Added botmove 3 to make the bot do the opposite of the owner
* - Added a hudtext to show player health, weapon, etc.
* - Access level changed from "m" to "p" ^_^
* - Removed mod specific definitions
* 1.40 - Changed owner input hooking style, making it possible for bots to walk and use impulses (flashlight, spray, ..)
* - A little tweaking here and there
* Credits:
* Botman - the source of PODBots helped alot while doing this
* Lord of Destruction - aim_at_origin() base taken from set_client_aiming()
* THE_STORM - more efficient msec calculations (from ePODbot)
* strelomet - display bot latency as "BOT" (from YaPB)
* Orangutanz - thanks for pointing out the few improvements;)
* Karko - thanks for helping with adding support for TS
************************************************************************************************/
#define ACCESS ADMIN_LEVEL_D //access level for bot commands, D is "p" in users.ini #define HUD_CHANNEL 4 //hud channel for showing bot info
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
new const plugin[] = "Potti"
new const version[] = "1.40"
new const author[] = "p3tsin"
new using_mod //1: cstrike, 2: specialists, 0: other
new money_offset //didnt want to include cstrike just for cs_get_user_money, so i used fakemeta
new botindex[33], botowner[33]
new botinfo[33], hasmenu[33]
new copymovements[33]
new bool:fullcontrol[33]
new Float:botmove[33][3]
new Float:botangles[33][3]
new botbuttons[33]
new botimpulses[33]
new botsnum
new gMsg_ShowMenu
new gMsg_TextMsg
new cv_hudcolor
new cv_hudpos
new fow_serverframe
new fow_cmdstart
new fow_clientkill
/* botinfos */
enum — { info_none = 0, info_motdnew slotsnum
new slotmenukeys
new currentweapon[33][3]
new weapon_slots[6][32]
const — TASK_BOTINFO = 54387 //random task id for check_botinfo/* commands which are not blocked/executed in fullcontrol mode */
new const fullcontrol_cmds[7][] = {
"amx_botadd", "amx_botexec", "amx_botmove", "amx_botcmds", "amx_botdel", "specmode", "follow"
// ================================================================================================
// ================================================================================================
#define get_user_money(%1) get_pdata_int(%1,money_offset) #define Distance2D(%1,%2) floatsqroot((%1*%1) + (%2*%2)) #define Radian2Degree(%1) (%1 * 180.0 / M_PI) //(%1 * 360.0 / (2 * M_PI))
// ================================================================================================
// ================================================================================================
public plugin_init() {
register_plugin(plugin,version,author) register_clcmd("amx_botadd", "makebot",ACCESS, "<name> — Create a new bot") register_clcmd("amx_botexec", "execbot",ACCESS, "<cmd> — Execute a command on your bot") register_clcmd("amx_botmove", "movebot",ACCESS, "<0-2> — Make the bot copy your movements") register_clcmd("amx_botcmds", "cmdsbot",ACCESS, "<0/1> — Send all commands to your bot") register_clcmd("amx_botdel", "removebot",ACCESS, "- Kicks your bot") register_clcmd("say", "catch_say") register_clcmd("say_team", "catch_say") static mod[20] get_modname(mod,19) if(equal(mod,"cstrike") || equal(mod,"czero") || equal(mod,"csv15") || equal(mod,"cs13")) using_mod = 1 else if(equal(mod,"ts")) using_mod = 2 register_event("DeathMsg", "event_deathmsg", "a") register_event("MOTD", "event_motd", "b") register_event("ResetHUD", "event_resethud", "be") register_event("ShowMenu", "event_showmenu", "b") register_event("TextMsg", "event_textmsg", "b", "1=4") //take only print_center switch(using_mod) { //mod specific events case 1: register_event("Spectator", "event_spectator", "a", "2=1") case 2: { register_event("ClipInfo", "event_clipinfo", "be") register_event("WeaponInfo", "event_weaponinfo", "be") //ts_getuserwpn didnt work right (for a bot) register_event("WStatus", "event_wstatus", "be") //updates pev_weapons to make slotmenu work } } gMsg_ShowMenu = get_user_msgid("ShowMenu") gMsg_TextMsg = get_user_msgid("TextMsg") cv_hudcolor = register_cvar("potti_hudcolor","50 150 200") cv_hudpos = register_cvar("potti_hudpos","0.82 0.85")
public— plugin_cfg() {
switch(using_mod) { //mod specific weapons and other settings case 1: { slotsnum = 5 set_weaponslot(1,{ 3,5,7,8,12,13,14,15,18,19,20,21,22,23,24,27,28,30,0 }) set_weaponslot(2,{ 1,10,11,16,17,26,0 }) set_weaponslot(3,{ 29,0 }) set_weaponslot(4,{ 4,9,25,0 }) set_weaponslot(5,{ 6,0 }) money_offset = is_amd64_server() ? 140 : 115 } case 2: { slotsnum = 6 set_weaponslot(1,{ 24,25,34,35,0 }) set_weaponslot(2,{ 1,9,12,14,22,28,31,0 }) set_weaponslot(3,{ 3,6,7,17,19,23,0 }) set_weaponslot(4,{ 4,5,11,13,15,18,20,26,27,32,33,0 }) set_weaponslot(5,{ 8,10,16,21,30,0 }) set_weaponslot(6,{ 36,0 }) //kungfu id is 0, so i had to make a special case for it }
// default: slotsnum = 0
} for(new i = 0; i < slotsnum; i++) slotmenukeys |= (1<<i) set_weaponslot(slot,weapons[]) { slot— new len = strlen(weapons) for(new i = 0; i < len; i++) weapon_slots[slot][i] = weapons[i]
public— client_disconnect(id) {
if(is_user_bot(id)) { new owner = botowner[id] if(owner) { copymovements[owner] = 0, botowner[id] = 0 botindex[owner] = 0 if(!—botsnum) bot_delforwards() } } else { new bot = botindex[id] if(is_user_connected(bot)) server_cmd("kick #%d",get_user_userid(bot)) fullcontrol[id] = false, copymovements[id] = 0 hasmenu[id] = 0, botindex[id] = 0 }
// ================================================================================================
// ================================================================================================
public makebot(id,level,cid) {
if(!cmd_access(id,level,cid,2)) return PLUGIN_HANDLED /*if(is_user_connected(botindex[id])) { console_print(id, "[%s] You already have a bot connected!",plugin) return PLUGIN_HANDLED }*/ static name[32] read_args(name,31) remove_quotes(name) trim(name) new bot = engfunc(EngFunc_CreateFakeClient,name) if(!bot) { console_print(id, "[%s] Couldn't create a bot, server full?",plugin) return PLUGIN_HANDLED } engfunc(EngFunc_FreeEntPrivateData,bot) bot_settings(bot) if(!botsnum) bot_addforwards() static szRejectReason[128] dllfunc(DLLFunc_ClientConnect,bot,name,"127.0.0.1",szRejectReason) if(!is_user_connected(bot)) { if(!botsnum) bot_delforwards() console_print(id, "[%s] Connection rejected: %s",plugin,szRejectReason) return PLUGIN_HANDLED } dllfunc(DLLFunc_ClientPutInServer,bot) set_pev(bot,pev_spawnflags, pev(bot,pev_spawnflags) | FL_FAKECLIENT) set_pev(bot,pev_flags, pev(bot,pev_flags) | FL_FAKECLIENT) reset_controls(id) fullcontrol[id] = false, copymovements[id] = 0 botindex[id] = bot, botowner[bot] = id hasmenu[id] = 0, botsnum++ console_print(id, "[%s] Bot successfully created! Id: %d, name: %s",plugin,bot,name) return PLUGIN_HANDLED
public— execbot(id,level,cid) {public— movebot(id,level,cid) {public— cmdsbot(id,level,cid) {public— removebot(id,level,cid) {
if(!cmd_access(id,level,cid,2)) return PLUGIN_HANDLED new bot = botindex[id] if(!is_user_connected(bot)) { console_print(id, "[%s] You have no bot connected..",plugin) return PLUGIN_HANDLED } static cmd[32], name[32] read_argv(1, cmd,31) get_user_name(bot, name,31) if(equal(cmd,"say") || equal(cmd,"say_team")) { static saytext[128], team new len = strlen(cmd) + 1 read_args(saytext,127) if(equal(cmd,"say_team")) team = 1 bot_say(bot,team,saytext[len]) console_print(id, "[%s] Executed on %s: %s %s",plugin,name,cmd,saytext) return PLUGIN_HANDLED } static args[2][32] new num = read_argc()-2 args[0][0] = 0, args[1][0] = 0 for(new i = 0; i < num; i++) read_argv(i+2, args[i],31) if(equal(cmd,"kill")) dllfunc(DLLFunc_ClientKill, bot) else if(equal(cmd,"name")) set_user_info(bot,"name",args[0]) else if(equal(cmd,"model")) set_user_info(bot,"model",args[0]) else bot_command(bot,cmd,args[0],args[1]) console_print(id, "[%s] Executed on %s: %s %s %s",plugin,name,cmd,args[0],args[1]) return PLUGIN_HANDLED
if(!cmd_access(id,level,cid,2)) return PLUGIN_HANDLED if(!is_user_connected(botindex[id])) { console_print(id, "[%s] You have no bot connected..",plugin) return PLUGIN_HANDLED } static arg[2], message[64] read_argv(1, arg,1) new num = str_to_num(arg) if(num < 0 || num > 3) num = 0 switch(num) { case 0: formatex(message,63, "not copying your moves at all") case 1: formatex(message,63, "copying your movements") case 2: formatex(message,63, "copying your movements and aiming the same spot when shooting") case 3: formatex(message,63, "copying your movements in reverse mode") } copymovements[id] = num console_print(id, "[%s] Your bot is now %s",plugin,message) return PLUGIN_HANDLED
if(!cmd_access(id,level,cid,2)) return PLUGIN_HANDLED if(!is_user_connected(botindex[id])) { console_print(id, "[%s] You have no bot connected..",plugin) return PLUGIN_HANDLED } new arg[2] read_argv(1, arg,1) new bool:value = str_to_num(arg) ? true : false fullcontrol[id] = value console_print(id, "[%s] Fullcontrol mode: %s",plugin,value?"ON (you can't control yourself now)":"OFF") slotmenu(id,value) return PLUGIN_HANDLED
if(!cmd_access(id,level,cid,1)) return PLUGIN_HANDLED new bot = botindex[id] if(is_user_connected(bot)) { static name[32] get_user_name(bot, name,31) console_print(id, "[%s] Bot %s kicked!",plugin,name) server_cmd("kick #%d",get_user_userid(bot)) } else console_print(id, "[%s] You have no bot connected..",plugin) return PLUGIN_HANDLED
// ================================================================================================
// ================================================================================================
public check_botinfo() {
static text[192], len, weapon static players[32], inum, bot, owner get_players(players,inum, "dh") static temp[3][10], color[3], Float:pos[2], i get_pcvar_string(cv_hudcolor, text,20) parse(text, temp[0],3, temp[1],3, temp[2],3) for(i = 0; i < 3; i++) color[i] = str_to_num(temp[i]) get_pcvar_string(cv_hudpos, text,20) parse(text, temp[0],9, temp[1],9) for(i = 0; i < 2; i++) pos[i] = floatstr(temp[i]) new Float:holdtime = (using_mod == 2) ? 120.0 : 1.0 //TS handles hudmessages differently set_hudmessage(color[0],color[1],color[2], pos[0],pos[1], 0, 0.0, holdtime, 0.0, 0.2, HUD_CHANNEL) static bool:was_alive[33] static name[32], weaponname[20], clip, ammo for(i = 0; i < inum; i++) { bot = players[i], owner = botowner[players[i]] if(!owner) continue //no owner, so this might be some other bot 😮 switch(botinfo[owner]) { case info_motd: { set_pev(bot,pev_button,IN_ATTACK) //client_print(owner,print_chat, "[%s] Note: a MOTD window was closed on your bot",plugin) } } botinfo[owner] = info_none if(!is_user_alive(bot)) { if(was_alive[bot]) { //hide hudmessage after death (mainly for TS) //show_hudmessage(owner,"^n") was_alive[bot] = false } continue } was_alive[bot] = true if(using_mod == 2) weapon = get_ts_weapon(bot,clip,ammo) else weapon = get_user_weapon(bot,clip,ammo) get_user_name(bot, name,31) get_weaponname(weapon, weaponname,19) if(using_mod == 1) copy(weaponname,19, weaponname[7]) //cut off the "weapon_" tag len = formatex(text,191, "Name: %s, Health: %d",name,get_user_health(bot)) len += formatex(text[len],191-len, "^nWeapon: %s, Ammo: %d/%d",weaponname,clip,ammo) if(using_mod == 1) formatex(text[len],191-len, "^nMoney left: $%d",get_user_money(bot)) //show_hudmessage(owner,"%s",text) }
public— client_command(id) {
new bot = botindex[id] if(!bot) return PLUGIN_CONTINUE static cmd[32] read_argv(0, cmd,31) new menu = hasmenu[id] if(menu && equal(cmd,"menuselect")) { new num[3] //no need to check if menu even has this key, read_argv(1, num,2) //coz menuselect wont be called if it doesnt hasmenu[id] = 0 if(fullcontrol[id]) slotmenu(id,true) switch(menu) { case 1: engclient_cmd(bot,"menuselect",num) case 2: change_to_weapon(bot,str_to_num(num)) } return PLUGIN_HANDLED } else if(fullcontrol[id]) { new num = sizeof(fullcontrol_cmds) for(new i = 0; i < num; i++) { if(!equali(cmd,fullcontrol_cmds[i])) continue return PLUGIN_CONTINUE } if(!contain(cmd,"say")) { static saytext[128], team read_args(saytext,127) remove_quotes(saytext) if(equal(cmd,"say_team")) team = 1 bot_say(bot,team,saytext) } else { static arg1[32], arg2[32] read_argv(1, arg1,95) read_argv(2, arg2,31) bot_command(bot,cmd,arg1,arg2) } return PLUGIN_HANDLED } return PLUGIN_CONTINUE
// ================================================================================================
// ================================================================================================
public fm_cmdstart(id,uc_handle,random_seed) {
new bot = botindex[id], movement = copymovements[id] if(!bot || !movement) return FMRES_IGNORED new alive = is_user_alive(id), button = get_uc(uc_handle,UC_Buttons) if(!alive) set_uc(uc_handle,UC_Buttons, button & ~IN_JUMP & ~IN_ATTACK & ~IN_ATTACK2 & ~IN_FORWARD & ~IN_BACK & ~IN_MOVELEFT & ~IN_MOVERIGHT) if(is_user_alive(bot)) { get_uc(uc_handle,UC_ForwardMove, botmove[id][0]) get_uc(uc_handle,UC_SideMove, botmove[id][1]) get_uc(uc_handle,UC_UpMove, botmove[id][2]) static Float:angles[3] if(movement == 2 && alive && button&IN_ATTACK) { static Float:target[3] get_user_aim(id,target) aim_at_origin(bot,target,angles) } else { get_uc(uc_handle,UC_ViewAngles, angles) if(movement == 3 && alive) { angles[1] += (angles[1] < 180.0) ? 180.0 : -180.0 if(button&IN_JUMP) button = button & ~IN_JUMP | IN_DUCK else if(button&IN_DUCK) button = button & ~IN_DUCK | IN_JUMP } } botangles[id][0] = angles[0] botangles[id][1] = angles[1] botangles[id][2] = angles[2] botbuttons[id] = button botimpulses[id] = get_uc(uc_handle,UC_Impulse) } return FMRES_IGNORED
public— fm_serverframe() {public— fm_clientkill(id) {
static players[32], inum get_players(players,inum, "dh") static Float:msecval global_get(glb_frametime, msecval) new msec = floatround(msecval * 1000.0) static bot, owner, Float:angles[3] for(new i = 0; i < inum; i++) { bot = players[i], owner = botowner[bot] if(!owner) continue //no owner, so this might be some other bot 😮 if(is_user_alive(bot)) { angles[0] = botangles[owner][0] angles[1] = botangles[owner][1] angles[2] = botangles[owner][2] set_pev(bot,pev_v_angle,angles) angles[0] /= -3.0 set_pev(bot,pev_angles,angles) } engfunc(EngFunc_RunPlayerMove,bot,angles,botmove[owner][0],botmove[owner][1],botmove[owner][2],botbuttons[owner],botimpulses[owner],msec) } return FMRES_IGNORED
new bot = botindex[id] if(bot && fullcontrol[id]) { if(is_user_alive(bot)) dllfunc(DLLFunc_ClientKill, bot) else console_print(id, "[%s] Can't suicide — your bot is already dead",plugin) return FMRES_SUPERCEDE } return FMRES_IGNORED
// ================================================================================================
// ================================================================================================
public catch_say(id) {
if(!botsnum) return static cmd[10], said[128] read_argv(0, cmd,9) read_args(said,127) remove_quotes(said) new teamsay = equal(cmd,"say_team") ? 1 : 0 handle_saytext(id,said,teamsay)
public— event_deathmsg() {public— event_motd(id) {public— event_resethud(id) {public— event_showmenu(id) {public— event_spectator() {public— next_spectarget(id) {public— event_textmsg(id) {public— event_clipinfo(id) {public— event_weaponinfo(id) {public— event_wstatus(id) {
new id = read_data(2) if(!botindex[id]) { id = botowner[id] if(id) reset_controls(id) return } new len static message[128] if(copymovements[id]) len = formatex(message,127, "botmove (you can't control yourself completely), ") if(fullcontrol[id]) len += formatex(message[len],127-len, "botcmds (all of your commands are sent to the bot)") else if(len) message[len-2] = 0 if(len) ucfirst(message) else formatex(message,127, "None") client_print(id,print_chat, "[%s] You have the following modes enabled:",plugin) client_print(id,print_chat, "* %s",message)
new owner = botowner[id] if(owner) botinfo[owner] = info_motd
new owner = botowner[id] if(owner) { //client_print(owner,print_chat, "[%s] Your bot has spawned",plugin) if(using_mod == 2) { set_pev(id,pev_weapons,0) currentweapon[id][0] = 0 currentweapon[id][1] = 0 currentweapon[id][2] = 0 } }
new owner = botowner[id] if(!owner) return static menu[32] new keys = read_data(1) read_data(4, menu,31) if(!strlen(menu)) return hasmenu[owner] = 1 //keypress is catched in client_command() message_begin(MSG_ONE,gMsg_ShowMenu,{0,0,0},owner) write_short(keys) write_char(-1) write_byte(0) write_string(menu) message_end() //client_print(owner,print_chat, "[%s] A menu was opened on your bot: %s",plugin,menu)
new id = read_data(1) if(copymovements[id]) { client_cmd(id, "specmode 4") //first person set_task(0.3,"next_spectarget",id) }
new bot = botindex[id] if(is_user_alive(bot)) { static name[32] get_user_name(bot, name,31) client_cmd(id, "follow ^"%s^"",name) }
new owner = botowner[id] if(!owner || !is_user_alive(id)) return static message[32] new datanum = read_datanum() read_data(2, message,31) message_begin(MSG_ONE,gMsg_TextMsg,{0,0,0},owner) write_byte(4) write_string(message) for(new i = 3; i < datanum; i++) { read_data(i, message,31) write_string(message) } message_end() return
if(botowner[id]) currentweapon[id][1] = read_data(1)
if(botowner[id]) { currentweapon[id][0] = read_data(1) currentweapon[id][1] = read_data(2) currentweapon[id][2] = read_data(3) }
new owner = botowner[id] if(!owner) return new wp = read_data(1), status = read_data(2) new wlist = pev(id,pev_weapons), weapon = (1<<wp) if(status) set_pev(id,pev_weapons, wlist | weapon) else set_pev(id,pev_weapons, wlist & ~weapon) return
// ================================================================================================
// ================================================================================================
stock get_user_aim(id,Float:aimorig[3]) {
static Float:origin[3], Float:view_ofs[3] pev(id,pev_origin, origin) pev(id,pev_view_ofs, view_ofs) origin[0] += view_ofs[0] origin[1] += view_ofs[1] origin[2] += view_ofs[2] static Float:vec[3] pev(id,pev_v_angle, vec) engfunc(EngFunc_MakeVectors,vec) global_get(glb_v_forward,vec) vec[0] = origin[0] + vec[0] * 9999.0 vec[1] = origin[1] + vec[1] * 9999.0 vec[2] = origin[2] + vec[2] * 9999.0 static line engfunc(EngFunc_TraceLine,origin,vec,0,id,line) get_tr2(line,TR_vecEndPos, aimorig)
stock— aim_at_origin(id,const Float:origin[3],Float:angles[3]) { //stock base by Lord of Destructionstock— change_to_weapon(id,slot) {stock— get_ts_weapon(id,&clip,&ammo) {
static Float:DeltaOrigin[3], Float:orig[3] pev(id,pev_origin, orig) DeltaOrigin[0] = orig[0] — origin[0] DeltaOrigin[1] = orig[1] — origin[1] DeltaOrigin[2] = orig[2] — origin[2] + 16.0 //bot keeps aiming too high angles[0] = Radian2Degree(floatatan(DeltaOrigin[2] / Distance2D(DeltaOrigin[0], DeltaOrigin[1]),0)) angles[1] = Radian2Degree(floatatan(DeltaOrigin[1] / DeltaOrigin[0],0)) if(DeltaOrigin[0] >= 0.0) angles[1] += 180.0
if(slot > slotsnum) return 0 if(using_mod == 2 && slot == 6) { engclient_cmd(id,"weapon_0") return 1 } new wnum static weapons[32], a get_user_weapons(id,weapons,wnum) new weapon, num = strlen(weapon_slots[—slot]) for(new i = 0; i < wnum; i++) { for(a = 0; a < num; a++) { if(weapons[i] != weapon_slots[slot][a]) continue weapon = weapons[i] break } if(weapon) break } if(weapon) { static weaponname[20] if(using_mod == 2) formatex(weaponname,19, "weapon_%d",weapon) else get_weaponname(weapon,weaponname,19) engclient_cmd(id,weaponname) return 1 } return 0
if(!is_user_bot(id)) return 0 clip = currentweapon[id][1] ammo = currentweapon[id][2] return currentweapon[id][0]
// ================================================================================================
// ================================================================================================
handle_saytext(id,said[],teamsay) { static name[32], text[192], teamname[20] get_user_name(id, name,31) new len, alive = is_user_alive(id) if(!alive) len = formatex(text,191, "*DEAD*") if(teamsay) { get_user_team(id, teamname,19) len += formatex(text[len],191-len, "(%s)",teamname) } else teamname[0] = 0 if(len) text[len++] = ' ' //add a space;) formatex(text[len],191-len, "%s : %s",name,said) static players[32], inum, owner, flags[5] formatex(flags,4, "dh%c%c",alive ? 'a' : 'b',teamsay ? 'e' : 0) get_players(players,inum, flags,teamname) //teamname is set only if teamsay is on for(new i = 0; i < inum; i++) { owner = botowner[players[i]] if(owner && alive != is_user_alive(owner)) client_print(owner,print_chat, "%s",text) } bot_command(id,cmd[],arg1[]="",arg2[]="") { if(strlen(arg2)) engclient_cmd(id,cmd,arg1,arg2) else if(strlen(arg1)) engclient_cmd(id,cmd,arg1) else engclient_cmd(id,cmd) slotmenu(id,bool:mode) { hasmenu[id] = mode ? 2 : 0 message_begin(MSG_ONE,gMsg_ShowMenu,{0,0,0},id) write_short(mode ? slotmenukeys : 0) write_char(-1) write_byte(0) write_string("^n") message_end() bot_addforwards() { fow_cmdstart = register_forward(FM_CmdStart,"fm_cmdstart",0) fow_serverframe = register_forward(FM_StartFrame,"fm_serverframe",1) fow_clientkill = register_forward(FM_ClientKill,"fm_clientkill",0) set_task(1.0,"check_botinfo",TASK_BOTINFO, "",0, "b") bot_delforwards() { unregister_forward(FM_CmdStart,fow_cmdstart,0) unregister_forward(FM_StartFrame,fow_serverframe,1) unregister_forward(FM_ClientKill,fow_clientkill,0) remove_task(TASK_BOTINFO) bot_settings(id) { set_user_info(id, "model", "gordon") set_user_info(id, "rate", "3500") set_user_info(id, "cl_updaterate", "30") set_user_info(id, "cl_lw", "0") set_user_info(id, "cl_lc", "0") set_user_info(id, "tracker", "0") set_user_info(id, "cl_dlmax", "128") set_user_info(id, "lefthand", "1") set_user_info(id, "friends", "0") set_user_info(id, "dm", "0") set_user_info(id, "ah", "1") set_user_info(id, "*bot", "1") set_user_info(id, "_cl_autowepswitch", "1") set_user_info(id, "_vgui_menu", "0") //disable vgui so we dont have to set_user_info(id, "_vgui_menus", "0") //register both 2 types of menus 🙂 bot_say(id,team,text[]) { engclient_cmd(id,team ? "say_team" : "say",text) handle_saytext(id,text,team) reset_controls(owner) { botmove[owner][0] = 0.0 botmove[owner][1] = 0.0 botmove[owner][2] = 0.0 botangles[owner][0] = 0.0 botangles[owner][1] = 0.0 botangles[owner][2] = 0.0 botbuttons[owner] = 0 botimpulses[owner] = 0 Плагин для создания ботов Скачать AmxModX 1.8 одним архивом и все необходимые файлы для работы с Amxmodx
Урок 7. Что такое алгоритм и как его составить.
get_client_listen— Функция проверяет слышит ли игрок другого игрока или нетis_user_bot— Функция проверяет игрок это или ботget_playersnum— Функция получает количество человек на сервереarwel— — Круто.Я всегда использую pod ботов для метамода. А еще у меня сслыка http://forums.alliedmods.net/showthread.php?t=11756 . Там написано как создать npc только я еще не совсем понимаю)) Maksovich — Неплохо. Пробовал запускать на домашнем сервере. Тормозит только все что-то. ФПС падает. Ресурсов чтоли много хавает непойму а так вроде нормально. 33_RUS_Wolf — good gavrilyuc — чё то от него у меня сервак слетает… Radius 7.05.2013 В 2011 году был опубликован скрипт, так что все может быть 🙂
Ограничение использования команды,функции в одном раунде
Ограничение использования команды,функции в одном раунде Готовые скрипты Вопрос "Как ограничить использование команды в одном раунде?", вследствие появления кучи различных режимов для DeathRun мода, встает очень часто у многих новичков. Описание много раз давалось, переделывались многие плагины. но вопрос все по-прежнему остается актуальным. По этому я и решил показать универсальный метод ограничения использования команды или функции в одном раунде. Для реализации данного кода, вы должны уметь работать со следующими функциями: plugin_init
register_event
register_cvar
get_cvar_num
register_clcmd— — Желательно.client_print— — Желательно
Ну что, освоили теорию по необходимым данным? Тогда идем дальше: Немного теории: С начала нам нужно немного подумать и прикинуть, как же должен работать плагин. Выбросите из головы функции и проговорите в слух, что должно происходить и в какой момент, и записывайте все по пункам. Вот что получилось у меня: Игрок первый раз использовал функцию в раунде Необходимо это дело запомнить куда то, записать Игрок использовал второй раз функцию в том же раунде Снова записываем Игрок вновь, в третий раз хочет использовать функцию, но мы хотим разрешить только 2 раза. Значит нужно перед каждым использованием проверять, сколь раз уж использовал игрок эту функцию. Если игрок уже не может использовать функцию, надо вывести какое то сообщение. Каждый раунд счетчик использования функции нужно обнулять А что вышло у вас? Пишите в комментариях, интересно же ) Не стесняйтесь, может что вместе придумаем Теперь нам нужно на эту грубую схему найти управу в виде функций, давайте прикинем: Нам нужна функция которую игрок использует, или создать свою ( у нас будет test_cmd) Нам нужна глобальная переменная для счетчика использования функции ( тоже легко new g_F_counter[33] = 0) Прибавлять к переменной по единице когда игрок использует функцию (g_F_counter[id]++) Необходимо создать условие ограничения if(g_F_counter[id]
Ограничение использования команд,функций по раундам
Ограничение использования команд,функций по раундам Готовые скрипты В последнее время очень часто спрашивают: "Как сделать, что бы команду можно было использовать не чаще чем в N раундов?" В принципе ни чего сложного нет, нужно всего лишь проверять сколько прошло раундов с последнего использования и в зависимости от количества разрешать или запрещать использования функции. Для организации это процесса нам потребуется знание следующих функций: plugin_init
register_event
register_cvar
register_clcmd - Не обязательно, но желательно.
get_cvar_num
client_print— — Тоже не обязательно.
Немного простой теории: Раз вы читаете эту строчку, будем считать что вы изучили необходимые функции и готовы приступить к изучению процесса ограничения использования чего либо по раундам. Для начала давайте подумаем, что должна делать функция и что для этого надо: Она должна считать количество раундов,для этого нам надо: Отловить момент начала раунда Прибавить к счетчику раундов 1 Из выше сказанного возникает переменная, в которой будем считать количество раундов Должна быть функция, где мы будем проверять, можно или нет использовать команду которую хотим ограничить Для удобной настройки, должен быть квар, изменяющий ограничение по раундам Вроде все, или что то забыл? Как вы думаете? Попробуйте отвернуться и на листочке сами продумать что вам для этого нужно. Понимание процесса — залог успеха. Теперь нужно подумать, как все это будет работать, так как если мы не представляем себе как зажечь спичку, мы ее можем долго чиркать о коробок где нет специальной полоски и спичка не зажжется. Тут примерно так же. Каждый раунд прибавлять по единице к глобальной переменной ( глобальная — зарегистрированная не внутри функции, и ее вызвать может любая функция в любой момент времени) Когда мы захотим использовать функцию, которую хотим ограничить, нужно сделать проверку: Прошло ли необходимое количество раундов, после последнего использования Если да то: Выполнить команду и обнулить счетчик, что бы он заново начал считать раунды Если нет то: Вывести сообщение о том через сколько раундов можно будет использовать данную команду, функцию и т.д. Ну как, понимание немного приходит? В обще pawn очень логичный язык и все монжно начертить на бумаге, попробуйте, мне лично часто помогает способ рисования работы функции на бумаге. Приступим к написанию плагина: Первое что нам нужно сделать — отловить события начала раунда с помощью функции register_event, которую необходимо использовать внутри plugin_init. Затем сразу же зарегистрируем квар, для удобной настройки плагина с помощью register_cvar, и тестовую команду с помощью register_clcmd и наконец получим в глобальную переменную значение нашего квара. В итоге получим следующий код:
/* Plugin generated by AMXX-Studio */
#include <amxmodx>
#include <amxmisc>
#define PLUGIN "Restriction of usage of function on rounds" #define VERSION "1.0" #define AUTHOR "Admin"
//Регистрация глобальной переменной для нашего счетчика
new g_Round_counter
public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR) //не имеет прямого отношения к нашей задачи register_event("HLTV", "RoundStart", "a", "1=0", "2=0") register_cvar("amx_f_every_rounds","3") register_clcmd("say /test","Counter_check") g_Round_counter = get_cvar_num("amx_f_every_rounds") На этом конфиг плагина готов, идем дальше. Создаем функцию внутри которой к нашей переменной, в момент начала нового раунда будет прибавляться по единице
public— RoundStart(){public— Counter_check(id){
//g_Round_counter++ тоже самое что g_Round_counter = g_Round_counter + 1 g_Round_counter++ И завершает наш плагин тестовая функция с проверкой на ограничение, которая вызывается с помощью команды say /test. Внутри которой есть условие: Если счетчик больше или равен квару, то выполняется код( это значит что прошло необходимое количество раундов что бы использовать команду или функцию и т.п.). Для примера там вывод текстового сообщения, и обязательно обнуляем счетчик. Что не даст еще раз использовать в этом раунде нашу команду и еще ближайшие 3 ( такое значение нашего квара). Если же вы хотите что бы на протяжении этого раунда можно было использовать функцию неограниченное число раз, то придется добавить еще переменных, и отловить событие конца раунда, и функции вызванной событием конца раунда проверять дополнительные переменные и если они отвечают условиям, то обнулять счетчик.
if(g_Round_counter >= get_cvar_num("amx_f_every_rounds")){ //add you code //Выполняется если счетчик достиг нужное значение или более //Для примера вывод сообщения client_print(id,print_chat,"Exec you function") g_Round_counter = 0 }else{ //add you code //Выполняется если счетчик не достиг нужного значения //Для примера вывод сообщения client_print(id,print_chat,"To use function wait %d rounds",get_cvar_num("amx_f_every_rounds")-g_Round_counter) } В место client_print(id,print_chat,"Exec you function") Можно сразу же поставить имя вашей функции например my_function(id) И скрипт перейдет к выполнению вашей функции. Внимание: Это при условии что вы все функции и переменные перенесли в свой плагин. Вот так, будет выглядеть весь код:
/* Plugin generated by AMXX-Studio */
#inc
- Войдите, чтобы ответить в этой теме.