/**
История изменений:
0.1 (26.07.2022) by b0t.
- Первый релиз;
0.2 (29.10.2022) by b0t.
- fix бага с подгрузкой большого кол-ва моделей;
0.4 (31.10.2022) by b0t.
- fix проверки на флаг доступа к пункту;
- Добавлен префикс к пункту меню если игрок не имеет к нему доступа;
- Мелкие правки кода;
*/
new const VERSION[] = "0.4";
#include <amxmodx>
#include <reapi_v>
new const g_szGlobalDir[] = "HatsMenu"; //Имя папки с настройками;
new const g_szSettingsFileName[] = "NewHatsMenu"; //Имя файла с настройками/MultiLang;
const HIDE_HATS_INDEX = 0xA553;
#define var_hats_index var_euser1
enum {
SECTION_MENU = 0,
MENU_NAME,
MODEL_PATH,
MODEL_SUB,
MODEL_SKIN,
MODEL_ANIMATION,
MODEL_FRAME_RATE,
ITEM_FLAG_ACCESS,
ITEM_NO_ACCESS_MSG,
NUMBER_OF_CELLS
};
enum _:CVARS_DATA {
CVAR__CMD_OPEN_MENU[128],
CVAR__FLAG_ACCESS_MENU[64],
CVAR__ABILITY_HIDE_HATS,
CVAR__HIDE_HATS_CMD[128],
CVAR__REMOVE_HATS_POST_DEATH,
CVAR__PRINT_CHAT_NEW_HATS
};
enum _:ARRAY_DATA {
ARRAY__SECTION_MENU[128],
ARRAY__MENU_NAME[128],
ARRAY__MODEL_PATH[256],
ARRAY__MODEL_SUB,
ARRAY__MODEL_SKIN,
ARRAY__MODEL_ANIMATION,
Float:ARRAY__MODEL_FRAME_RATE,
ARRAY__ITEM_FLAG_ACCESS,
ARRAY__ITEM_NO_ACCESS_MSG[128]
};
new
gCvars[CVARS_DATA],
Array:g_ArrayData,
Array:g_szArray__MenuIndex;
new
g_pCvarAbilityHideHats,
g_pCvarRemoveHatsPostDeath,
fwd_AddToFullPack,
HookChain:CBasePlayer_Killed;
new
p_iEntID[33],
bool:p_bToggleHats[33],
p_iHatsUse[33];
public plugin_precache() {
Func__CreateCvars();
Func__CreateFiles();
Func__CreateMultiLangFile();
for(new iItem,aData[ARRAY_DATA];iItem<ArraySize(g_ArrayData);iItem++) {
ArrayGetArray(g_ArrayData,iItem,aData);
precache_model(fmt("models/%s",aData[ARRAY__MODEL_PATH]));
}
}
public plugin_init() {
register_plugin("New Hats Menu",VERSION,"b0t.");
UTIL__RegisterClCmd(gCvars[CVAR__HIDE_HATS_CMD],"Func__ToggleHideHats");
UTIL__RegisterClCmd(gCvars[CVAR__CMD_OPEN_MENU],"Func__ShowMainHatsMenu_Pre");
if(gCvars[CVAR__ABILITY_HIDE_HATS])
fwd_AddToFullPack = register_forward(FM_AddToFullPack,"FM_AddToFullPack_Post",true);
hook_cvar_change(g_pCvarAbilityHideHats,"Func__HookCvarChange_AbilityHideHats");
hook_cvar_change(g_pCvarRemoveHatsPostDeath,"Func__HookCvarChange_RemoveHatsPostDeath");
if(gCvars[CVAR__REMOVE_HATS_POST_DEATH])
CBasePlayer_Killed = RegisterHookChain(RG_CBasePlayer_Killed,"CBasePlayer_Killed_Post", .post = true);
register_dictionary(fmt("%s.txt",g_szSettingsFileName));
}
public Func__ToggleHideHats(const id) {
p_bToggleHats[id] ^= true;
return PLUGIN_HANDLED;
}
public Func__ShowMainHatsMenu_Pre(const id) {
Func__ShowMainHatsMenu_Post(id,0);
return PLUGIN_HANDLED;
}
public Func__ShowMainHatsMenu_Post(const id,const iPages) {
if(gCvars[CVAR__FLAG_ACCESS_MENU][0]) {
if(~get_user_flags(id) & read_flags(gCvars[CVAR__FLAG_ACCESS_MENU])) {
client_print_color(id,print_team_default,"%L",LANG_SERVER,"NHM_CHAT_DONT_ACCESS");
return PLUGIN_HANDLED;
}
}
ArrayClear(g_szArray__MenuIndex);
new iBitFlags = get_user_flags(id);
new iMenu = menu_create(fmt("%L\R\d",LANG_SERVER,"NHM_MAIN_MENU_NAME"),"ShowMainHatsMenu_Post__Handler");
menu_additem(iMenu,fmt("%L%L",LANG_SERVER,!is_nullent(p_iEntID[id]) ? "NHM_COLOR_HATS_YES" : "NHM_COLOR_HATS_NO",LANG_SERVER,"NHM_ITEM_REMOVE_HATS"),"remove_hats");
for(new iItem,aData[ARRAY_DATA];iItem<ArraySize(g_ArrayData);iItem++) {
ArrayGetArray(g_ArrayData,iItem,aData);
if((ArrayFindString(g_szArray__MenuIndex,aData[ARRAY__SECTION_MENU])) != -1)
continue;
if(contain(aData[ARRAY__SECTION_MENU],"_") != -1) {
if(p_iHatsUse[id] == iItem) {
UTIL__ReplaceSimbols(aData[ARRAY__MENU_NAME],charsmax(aData),true);
add(aData[ARRAY__MENU_NAME],charsmax(aData),fmt(" %L",LANG_SERVER,"NHM_HATS_USE"));
}
if(aData[ARRAY__ITEM_FLAG_ACCESS] && ~iBitFlags & aData[ARRAY__ITEM_FLAG_ACCESS]) {
UTIL__ReplaceSimbols(aData[ARRAY__MENU_NAME],charsmax(aData),true);
add(aData[ARRAY__MENU_NAME],charsmax(aData),fmt(" %s",aData[ARRAY__ITEM_NO_ACCESS_MSG]));
}
menu_additem(iMenu,fmt("%s",aData[ARRAY__MENU_NAME]),fmt("_%i",iItem),aData[ARRAY__ITEM_FLAG_ACCESS]);
}
else {
ArrayPushString(g_szArray__MenuIndex,aData[ARRAY__SECTION_MENU]);
menu_additem(iMenu,fmt("%s",aData[ARRAY__SECTION_MENU]));
}
}
new iItem = menu_items(iMenu);
if(!iItem) {
client_print_color(id,print_team_default,"%L",LANG_SERVER,"NHM_CHAT_EMPTY_MENU");
menu_destroy(iMenu);
return PLUGIN_HANDLED;
}
if(iItem == 8)
menu_addblank2(iMenu);
if(iItem >= 8 && iItem <= 9) {
menu_setprop(iMenu,MPROP_EXIT,MEXIT_FORCE);
menu_setprop(iMenu,MPROP_PERPAGE,0);
}
UTIL__RegisterMenu(id,iMenu, .szExitName = "Выход", .szNumberColor = "\y", .iPage = iPages);
return PLUGIN_HANDLED;
}
public ShowMainHatsMenu_Post__Handler(const id,const iMenu,const iItem) {
if(iItem == MENU_EXIT) {
menu_destroy(iMenu);
return PLUGIN_HANDLED;
}
new iNewMenu = iMenu;
new iPage;
player_menu_info(id,iNewMenu,iNewMenu,iPage);
new szData[64],szName[256];
menu_item_getinfo(iMenu,iItem, .info = szData, .infolen = charsmax(szData), .name = szName, .namelen = charsmax(szName));
menu_destroy(iMenu);
if(equal(szData,"remove_hats")) {
Func__RemoveHats(id);
return Func__ShowMainHatsMenu_Post(id,iPage);
}
if(contain(szData,"_") != -1) {
replace_all(szData,charsmax(szData),"_","");
if(p_iHatsUse[id] == str_to_num(szData)) {
client_print_color(id,print_team_default,"%L",LANG_SERVER,"NHM_CHAT_HATS_USE");
return Func__ShowMainHatsMenu_Post(id,iPage);
}
new aData[ARRAY_DATA];
ArrayGetArray(g_ArrayData,str_to_num(szData),aData);
Func__CreateHats(id,str_to_num(szData));
Func__ShowMainHatsMenu_Post(id,iPage);
if(gCvars[CVAR__PRINT_CHAT_NEW_HATS]) {
UTIL__ReplaceSimbols(aData[ARRAY__MENU_NAME],charsmax(aData),true);
client_print_color(id,print_team_default,"%L",LANG_SERVER,"NHM_CHAT_PUT_HAT",aData[ARRAY__MENU_NAME]);
}
}
else
Func__ShowSecondaryHatsMenu(id,szName,0);
return PLUGIN_HANDLED;
}
public Func__ShowSecondaryHatsMenu(const id,const szName[],const iPages) {
new iMenu = menu_create(szName,"ShowSecondaryHatsMenu__Handler");
new iBitFlags = get_user_flags(id);
for(new iItem,aData[ARRAY_DATA];iItem<ArraySize(g_ArrayData);iItem++) {
ArrayGetArray(g_ArrayData,iItem,aData);
if(contain(aData[ARRAY__SECTION_MENU],"_") != -1)
continue;
if(!equal(aData[ARRAY__SECTION_MENU],szName))
continue;
if(p_iHatsUse[id] == iItem) {
UTIL__ReplaceSimbols(aData[ARRAY__MENU_NAME],charsmax(aData),true);
add(aData[ARRAY__MENU_NAME],charsmax(aData),fmt(" %L",LANG_SERVER,"NHM_HATS_USE"));
}
if(aData[ARRAY__ITEM_FLAG_ACCESS] && ~iBitFlags & aData[ARRAY__ITEM_FLAG_ACCESS]) {
UTIL__ReplaceSimbols(aData[ARRAY__MENU_NAME],charsmax(aData),true);
add(aData[ARRAY__MENU_NAME],charsmax(aData),fmt(" %s",aData[ARRAY__ITEM_NO_ACCESS_MSG]));
}
menu_additem(iMenu,fmt("%s",aData[ARRAY__MENU_NAME]),fmt("%i|%s",iItem,szName),aData[ARRAY__ITEM_FLAG_ACCESS]);
}
new iItem = menu_items(iMenu);
if(iItem == 8)
menu_addblank2(iMenu);
if(iItem >= 8 && iItem <= 9) {
menu_setprop(iMenu,MPROP_EXIT,MEXIT_FORCE);
menu_setprop(iMenu,MPROP_PERPAGE,0);
}
UTIL__RegisterMenu(id,iMenu, .szExitName = "В меню", .szNumberColor = "\y", .iPage = iPages);
return PLUGIN_HANDLED;
}
public ShowSecondaryHatsMenu__Handler(const id,const iMenu,const iItem) {
if(iItem == MENU_EXIT) {
menu_destroy(iMenu);
return Func__ShowMainHatsMenu_Pre(id);
}
new iMenuNew = iMenu;
new iPage;
player_menu_info(id,iMenuNew,iMenuNew,iPage);
new szData[64];
menu_item_getinfo(iMenu,iItem, .info = szData, .infolen = charsmax(szData));
menu_destroy(iMenu);
new szItem[64],szName[256];
strtok(szData,szItem,charsmax(szItem),szName,charsmax(szName),'|');
trim(szItem);
trim(szName);
if(p_iHatsUse[id] == str_to_num(szItem)) {
client_print_color(id,print_team_default,"%L",LANG_SERVER,"NHM_CHAT_HATS_USE");
return Func__ShowSecondaryHatsMenu(id,szName,iPage);
}
new aData[ARRAY_DATA];
ArrayGetArray(g_ArrayData,str_to_num(szItem),aData);
Func__CreateHats(id,str_to_num(szItem));
Func__ShowSecondaryHatsMenu(id,szName,iPage);
if(gCvars[CVAR__PRINT_CHAT_NEW_HATS]) {
UTIL__ReplaceSimbols(aData[ARRAY__MENU_NAME],charsmax(aData),true);
client_print_color(id,print_team_default,"%L",LANG_SERVER,"NHM_CHAT_PUT_HAT",aData[ARRAY__MENU_NAME]);
}
return PLUGIN_HANDLED;
}
public FM_AddToFullPack_Post(iEs,iE,iEnt,pHost,BitHostFlags,Player,pSet) {
if(!is_user_connected(pHost))
return FMRES_IGNORED;
if(p_bToggleHats[pHost] && get_entvar(iEnt,var_hats_index) == HIDE_HATS_INDEX)
set_es(iEs,ES_Effects,EF_NODRAW);
return FMRES_IGNORED;
}
public CBasePlayer_Killed_Post(const pVictim,const pAttacker) {
if(!is_nullent(p_iEntID[pVictim]))
Func__RemoveHats(pVictim);
}
public Func__HookCvarChange_AbilityHideHats(pCvar,const szOldValue[],const szNewValue[]) {
new iNewValue = str_to_num(szNewValue);
new iOldValue = str_to_num(szOldValue);
if(iNewValue != iOldValue) {
switch(iNewValue) {
case 1: fwd_AddToFullPack = register_forward(FM_AddToFullPack,"FM_AddToFullPack_Post",true);
case 0: {
unregister_forward(FM_AddToFullPack,fwd_AddToFullPack,true);
arrayset(p_bToggleHats,0,sizeof(p_bToggleHats));
}
}
}
}
public Func__HookCvarChange_RemoveHatsPostDeath(pCvar,const szOldValue[],const szNewValue[]) {
new iNewValue = str_to_num(szNewValue);
new iOldValue = str_to_num(szOldValue);
if(iNewValue != iOldValue) {
switch(iNewValue) {
case 1: EnableHookChain(CBasePlayer_Killed);
case 0: DisableHookChain(CBasePlayer_Killed);
}
}
}
public client_putinserver(id) {
p_iEntID[id] = p_iHatsUse[id] = -1;
p_bToggleHats[id] = false;
}
public Func__CreateHats(const id,const iItem) {
if(!is_nullent(p_iEntID[id]))
Func__RemoveHats(id);
new iEnt = rg_create_entity("info_target");
if(is_nullent(iEnt))
return;
new aData[ARRAY_DATA];
ArrayGetArray(g_ArrayData,iItem,aData);
engfunc(EngFunc_SetModel,iEnt,fmt("models/%s",aData[ARRAY__MODEL_PATH]));
set_entvar(iEnt,var_movetype,MOVETYPE_FOLLOW);
set_entvar(iEnt,var_aiment,id);
set_entvar(iEnt,var_body,aData[ARRAY__MODEL_SUB]);
set_entvar(iEnt,var_skin,aData[ARRAY__MODEL_SKIN]);
set_entvar(iEnt,var_sequence,aData[ARRAY__MODEL_ANIMATION]);
set_entvar(iEnt,var_framerate,aData[ARRAY__MODEL_FRAME_RATE]);
set_entvar(iEnt,var_hats_index,HIDE_HATS_INDEX);
p_iEntID[id] = iEnt;
p_iHatsUse[id] = iItem;
}
public Func__RemoveHats(const id) {
if(!is_nullent(p_iEntID[id]))
set_entvar(p_iEntID[id],var_flags,FL_KILLME);
p_iEntID[id] = p_iHatsUse[id] = -1;
}
public Func__CreateFiles() {
new szData[256];
formatex(szData,charsmax(szData),"addons/amxmodx/configs/plugins/%s",g_szGlobalDir);
if(!dir_exists(szData))
mkdir(szData);
add(szData,charsmax(szData),fmt("/%s.ini",g_szSettingsFileName));
if(!file_exists(szData))
write_file(szData,
"; Название секции где будет пункт | Имя в меню | Путь до модели | SubModel(Если есть) | Skin(Если есть) | Номер анимации(если есть) | Скорость анимации | Флаг доступа к пункту | Отображение если нету доступа^n\
; *Путь до модели указывать без корневой папки 'models'^n\
; *Если пункт необходимо добавить в основное меню, в имени секции указать '_'^n\
; *Если отсутствует SubModel/Skin/Анимация указать 0 ^n\
; *Если пункт будет доступен всем указать '_'^n\
; *Отображение если нету доступа, то что будет писаться если не будет доступа к пункту^n\
; Если не требуется указать '_'"
);
new f = fopen(szData,"r");
new aData[ARRAY_DATA];
new szFileData[NUMBER_OF_CELLS][256];
g_szArray__MenuIndex = ArrayCreate(256);
g_ArrayData = ArrayCreate(ARRAY_DATA);
new iLine;
while(!feof(f)) {
fgets(f,szData,charsmax(szData));
trim(szData);
iLine++;
if(szData[0] == ';' || szData[0] == EOS)
continue;
if(explode_string(szData," | ",szFileData,sizeof(szFileData),charsmax(szFileData[])) == NUMBER_OF_CELLS) {
copy(aData[ARRAY__SECTION_MENU],charsmax(aData),szFileData[SECTION_MENU]);
copy(aData[ARRAY__MENU_NAME],charsmax(aData),szFileData[MENU_NAME]);
copy(aData[ARRAY__MODEL_PATH],charsmax(aData),szFileData[MODEL_PATH]);
aData[ARRAY__MODEL_SUB] = str_to_num(szFileData[MODEL_SUB]);
aData[ARRAY__MODEL_SKIN] = str_to_num(szFileData[MODEL_SKIN]);
aData[ARRAY__MODEL_ANIMATION] = str_to_num(szFileData[MODEL_ANIMATION]);
aData[ARRAY__MODEL_FRAME_RATE] = str_to_float(szFileData[MODEL_FRAME_RATE]);
aData[ARRAY__ITEM_FLAG_ACCESS] = strcmp(szFileData[ITEM_FLAG_ACCESS],"_") != 0 ? read_flags(szFileData[ITEM_FLAG_ACCESS]) : ADMIN_ALL;
copy(aData[ARRAY__ITEM_NO_ACCESS_MSG],charsmax(aData),szFileData[ITEM_NO_ACCESS_MSG]);
if(!file_exists(fmt("models/%s",aData[ARRAY__MODEL_PATH]))) {
server_print("[HATS MENU] Модель не найдена! Пункт удалён! [models/%s]",aData[ARRAY__MODEL_PATH]);
continue;
}
ArrayPushArray(g_ArrayData,aData);
}
else {
server_print("[HATS MENU] Файл [NewHatsMenu.ini] заполнен не верно! Строка: %i",iLine);
pause("a");
break;
}
continue;
}
fclose(f);
}
public Func__CreateCvars() {
bind_pcvar_string(
create_cvar(
.name = "nhm_cmd_open_menu",
.string = "hats",
.description = "Команда для открытия меню(все чаты + консоль)"
),
gCvars[CVAR__CMD_OPEN_MENU],charsmax(gCvars)
);
bind_pcvar_string(
create_cvar(
.name = "nhm_flag_access_menu",
.string = "",
.description = "Флаг доступа к меню^nОставить пустым если доступно всем"
),
gCvars[CVAR__FLAG_ACCESS_MENU],charsmax(gCvars)
);
bind_pcvar_num(
g_pCvarAbilityHideHats = create_cvar(
.name = "nhm_ability_hide_hats",
.string = "1",
.description = "Возможность скрывать шапки^nВНИМАНИЕ: возможность использует FullPack"
),
gCvars[CVAR__ABILITY_HIDE_HATS]
);
bind_pcvar_string(
create_cvar(
.name = "nhm_hide_hats_cmd",
.string = "toggle_hats",
.description = "Команда для отключения/включения отображения шляп^nВсе чаты + консоль"
),
gCvars[CVAR__HIDE_HATS_CMD],charsmax(gCvars)
);
bind_pcvar_num(
g_pCvarRemoveHatsPostDeath = create_cvar(
.name = "nhm_remove_hats_post_death",
.string = "0",
.description = "Снимать шляпу с игрока после смерти"
),
gCvars[CVAR__REMOVE_HATS_POST_DEATH]
);
bind_pcvar_num(
create_cvar(
.name = "nhm_print_chat_new_hats",
.string = "1",
.description = "Выводить ли игроку в чат информацию о надетой шапке"
),
gCvars[CVAR__PRINT_CHAT_NEW_HATS]
);
AutoExecConfig(.name = g_szSettingsFileName, .folder = g_szGlobalDir);
}
public Func__CreateMultiLangFile() {
new szData[256];
formatex(szData,charsmax(szData),"addons/amxmodx/data/lang/%s.txt",g_szSettingsFileName);
if(file_exists(szData))
return;
write_file(szData,
"[ru]^n^n\
NHM_MAIN_MENU_NAME = Меню шапок^n^n\
NHM_COLOR_HATS_YES = \r^n\
NHM_COLOR_HATS_NO = \d^n\
NHM_ITEM_REMOVE_HATS = Снять шапку^^n^n^n\
NHM_HATS_USE = \r[\dИспользуется\r]^n^n\
NHM_CHAT_DONT_ACCESS = ^^3[^^4HATS^^3] ^^1У вас нет доступа^^4!^n\
NHM_CHAT_EMPTY_MENU = ^^3[^^4HATS^^3] ^^1Увы, в меню пусто^^4!^n\
NHM_CHAT_HATS_USE = ^^3[^^4HATS^^3] ^^1Шапка уже надета^^4!^n\
NHM_CHAT_PUT_HAT = ^^3[^^4HATS^^3] ^^1Вы надели ^^4%s"
);
}
public plugin_natives() {
register_native("nhm_get_user_hats","native_nhm_get_user_hats");
register_native("nhm_remove_user_hats","native_nhm_remove_user_hats");
register_native("nhm_get_user_show_hats","native_nhm_get_user_show_hats");
register_native("nhm_set_user_show_hats","native_nhm_set_user_show_hats");
}
public bool:native_nhm_get_user_hats() {
return bool:(!is_nullent(p_iEntID[get_param(1)]));
}
public native_nhm_remove_user_hats() {
Func__RemoveHats(get_param(1));
}
public bool:native_nhm_get_user_show_hats() {
return bool:(p_bToggleHats[get_param(1)]);
}
public native_nhm_set_user_show_hats() {
p_bToggleHats[get_param(1)] = bool:get_param(2);
}