Иконка ресурса

New Dance Menu 2.1

Нет прав для скачивания
Установка
  1. Скомпилируйте плагин (инструкция)
  2. Скопируйте скомпилированный .amxx в директорию /amxmodx/plugins/
  3. Пропишите .amxx в файле /amxmodx/configs/plugins.ini
  4. Смените карту или перезапустите сервер.
  5. Автоматически создаются файлы по следующим путям:
    1. /amxmodx/configs/Dance.ini
    2. /amxmodx/configs/plugins/Dance.cfg
    3. /amxmodx/data/lang/Dance.txt
  6. Настройте Dance.ini | Dance.cfg | Dance.txt по своему вкусу
  7. Скопируйте дополнительные файлы плагина (models) в соответствующие директории на сервере
  8. Смените карту или перезапустите сервер.
Исходный код
Код:
/**
    История изменений:
        1.0 (04.03.2022) by b0t.
            - Первый релиз;
      
        1.1 (05.03.2022) by b0t.
            - fix сброса анимации при движении;
            - Сброс анимации после респавна/после смерти;
      
        1.2 (07.03.2022) by b0t.
            - Доступ к меню только для живых игроков;
            - Сброс анимации в момент атаки/перезарядки;
            - Сброс анимации, если игрок по какой либо причине стал видимым;
            - Невозможность создать анимацию если недостаточно места для игрока;
            - Добавлена задержка перед использованием следующей аимации;
            - native;
            - Мультиязычность;
            - Мелкие правки кода;
      
        2.0 (16.03.2022) by b0t.
            - Изменён метод заполнения/чтения файла с настройками;
            - 'костыль' для микрофона в момент удаления анимации;
            - Поддержка мультименю;
            - Отказ от проверки 'Достаточно ли места';
*/
new const VERSION[] = "2.0";
#include <amxmodx>
#include <reapi>
#include <fakemeta>
#include <xs>
/**
    Файлы с настройками создаются автоматически:
    configs/plugins/Dance.cfg;
    configs/Dance.ini;
    amxmodx/data/lang/Dance.txt;
*/
#define var_ent_model       var_impulse
enum _:XYZ {
    Float:X,Float:Y,Float:Z
};
enum _:ArrayData {
    MENU_INDEX[256],
    MENU_NAME[256],
    MODEL_WAY[256],
    SEQUENCE,
    Float:FRAMERATE,
    FLAG_ACCESS
};
stock const ATTACK_BTN = IN_ATTACK|IN_ATTACK2|IN_RELOAD;
stock const g_szCamPrecache[] = "models/rpgrocket.mdl";
new
    Array:g_Array__Dance,
    Array:g_Array__MenuIndex,
    iCamIndex;
new
    g_pCvarrString__FlagAccess[64],
    g_pCvarString__ClCmdOpenMenu[256],
    g_pCvarNum__CamDistance,
    Float:g_pCvarFloat__Flood;
new
    p_iEntityId[33],
    p_iCamId[33],
    Float:p_fFloodDanceMenu[33];
public plugin_precache() {
    Func__ReadSettingsFile();
    new aData[ArrayData];
    for(new iItem;iItem<ArraySize(g_Array__Dance);iItem++) {
        ArrayGetArray(g_Array__Dance,iItem,aData);
        if(!file_exists(aData[MODEL_WAY])) {
            server_print("[NewDance] Bad load model: %s",aData[MODEL_WAY]);
            ArrayDeleteItem(g_Array__Dance,iItem);
        }
        else
            precache_model(aData[MODEL_WAY]);
    }
    if(!ArraySize(g_Array__Dance)) {
        server_print("[NewDance] No models not");
        server_print("[NewDance] Plugin is state pause");
      
        pause("a");
    }
    iCamIndex = precache_model(g_szCamPrecache);
}
public plugin_init() {
    register_plugin("New Dance Menu",VERSION,"b0t.");
    UTIL__RegisterClCmd(g_pCvarString__ClCmdOpenMenu,"Show__DanceMenu");
    RegisterHookChain(RG_CBasePlayer_Killed,"RG_CBasePlayerKilledAndSpawn_Post", .post = true);
    RegisterHookChain(RG_CBasePlayer_Spawn,"RG_CBasePlayerKilledAndSpawn_Post", .post = true);
    register_dictionary("Dance.txt");
}
public RG_CBasePlayerKilledAndSpawn_Post(const id) {
    if(p_iEntityId[id] != 0) {
        Func__RemoveModel(p_iEntityId[id]);
        Func__RemoveCam(p_iCamId[id]);
    }
}
public Show__DanceMenu(const id) {
    if(!(get_user_flags(id) & read_flags(g_pCvarrString__FlagAccess)) && g_pCvarrString__FlagAccess[0])
        return client_print(id,print_center,"%L",LANG_PLAYER,"CENTER_ONLY_ADMINS");
    if(!is_user_alive(id))
        return client_print(id,print_center,"%L",LANG_PLAYER,"CENTER_ONLY_ALIVE");
    new iMenu = menu_create(fmt("%L",LANG_PLAYER,"DANCE_MENU_NAME"),"DanceMenu__Handler");
    ArrayClear(g_Array__MenuIndex);
    for(new iItem,aData[ArrayData];iItem<ArraySize(g_Array__Dance);iItem++) {
        ArrayGetArray(g_Array__Dance,iItem,aData);
        if((ArrayFindString(g_Array__MenuIndex,aData[MENU_INDEX])) != -1)
            continue;
      
        if(contain(aData[MENU_INDEX],"_") != -1) {
            menu_additem(iMenu,fmt("%s",aData[MENU_NAME]),fmt("_%i",iItem), .paccess = aData[FLAG_ACCESS]);
        }
        else {
            ArrayPushString(g_Array__MenuIndex,aData[MENU_INDEX]);
            menu_additem(iMenu,fmt("%s",aData[MENU_INDEX]), .paccess = aData[FLAG_ACCESS]);
        }
    }
    UTIL__DisplayMenu(id,iMenu,"Выход");
  
    return PLUGIN_HANDLED;
}
public DanceMenu__Handler(const id,const iMenu,const iItem) {
    if(iItem == MENU_EXIT)
        return menu_destroy(iMenu);
  
    new szData[64],szName[256];
    menu_item_getinfo(iMenu,iItem, .info = szData, .infolen = charsmax(szData), .name = szName, .namelen = charsmax(szName));
    menu_destroy(iMenu);
    new aData[ArrayData];
    if(contain(szData,"_") != -1) {
        if(!UTIL__IsCreateDanceEntity(id))
            return Show__DanceMenu(id);
        replace_all(szData,charsmax(szData),"_","");
        ArrayGetArray(g_Array__Dance,str_to_num(szData),aData);
        Func__CreateModels(id,aData[MODEL_WAY],aData[SEQUENCE],aData[FRAMERATE]);
        p_fFloodDanceMenu[id] = get_gametime()+g_pCvarFloat__Flood;
        Show__DanceMenu(id);
    }
    else
        Show__DanceMenuNext(id,szName);
    return PLUGIN_HANDLED;
}
public Show__DanceMenuNext(const id,const szName[]) {
    new iMenu = menu_create(szName,"DanceMenuNew__Handler");
    for(new i,aData[ArrayData];i<ArraySize(g_Array__Dance);i++) {
        ArrayGetArray(g_Array__Dance,i,aData);
        if(contain(aData[MENU_INDEX],"_") != -1)
            continue;
      
        if(!equal(aData[MENU_INDEX],szName))
            continue;
          
        menu_additem(iMenu,fmt("%s",aData[MENU_NAME]),fmt("%i|%s",i,szName),aData[FLAG_ACCESS]);
    }
    UTIL__DisplayMenu(id,iMenu,"В меню");
    return PLUGIN_HANDLED;
}
public DanceMenuNew__Handler(const id,const iMenu,const iItem) {
    if(iItem == MENU_EXIT) {
        menu_destroy(iMenu);
        Show__DanceMenu(id);
        return PLUGIN_HANDLED;
    }
    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(!UTIL__IsCreateDanceEntity(id))
        return Show__DanceMenuNext(id,szName);
    new aData[ArrayData];
    ArrayGetArray(g_Array__Dance,str_to_num(szItem),aData);
    Func__CreateModels(id,aData[MODEL_WAY],aData[SEQUENCE],aData[FRAMERATE]);
    p_fFloodDanceMenu[id] = get_gametime()+g_pCvarFloat__Flood;
    Show__DanceMenuNext(id,szName);
    return PLUGIN_HANDLED;
}
public Func__CreateModels(const id,const szModel[],const iSequence,const Float:fFrameRate) {
    Func__RemoveModel(p_iEntityId[id]);
    Func__RemoveCam(p_iCamId[id]);
    new iEnt = rg_create_entity("info_target");
    new iEntModel = rg_create_entity("info_target");
    if(is_nullent(iEnt) || is_nullent(iEntModel))
        return;
    new Float:fOrigin[XYZ],Float:fMins[XYZ],Float:fAngles[XYZ];
    get_entvar(id,var_origin,fOrigin);
    get_entvar(id,var_mins,fMins);
    fMins[X] = fOrigin[X];
    fMins[Y] = fOrigin[Y];
    fMins[Z] += fOrigin[Z];
    engfunc(EngFunc_SetModel,iEnt,szModel);
    set_entvar(iEnt,var_movetype,MOVETYPE_FLY);
    set_entvar(iEnt,var_ent_model,iEntModel);
    set_entvar(iEnt,var_owner,id);
    p_iEntityId[id] = iEnt;
    set_entvar(iEntModel,var_movetype,MOVETYPE_FOLLOW);
    set_entvar(iEntModel,var_aiment,iEnt);
    set_entvar(iEnt,var_framerate,fFrameRate);
    set_entvar(iEnt,var_sequence,iSequence);
    get_entvar(id,var_angles,fAngles);
    fAngles[X] = 0.0;
    set_entvar(iEnt,var_angles,fAngles);
    engfunc(EngFunc_SetOrigin,iEnt,fMins);
    engfunc(EngFunc_SetOrigin,iEntModel,fMins);
    new szModelNew[256];
    get_user_info(id,"model",szModelNew,charsmax(szModelNew));
    format(szModelNew,charsmax(szModelNew),"models/player/%s/%s.mdl",szModelNew,szModelNew);
    engfunc(EngFunc_SetModel,iEntModel,szModelNew);
    set_entvar(iEntModel,var_body,get_entvar(id,var_body));
    set_entvar(iEntModel,var_skin,get_entvar(id,var_skin));
    set_entvar(iEnt,var_nextthink,get_gametime());
    SetThink(iEnt,"CBaseModel_Think_Post");
    rg_set_user_invisibility(id,true);
    new iEntCam = rg_create_entity("trigger_camera");
    if(is_nullent(iEntCam))
        return;
  
    set_entvar(iEntCam,var_modelindex,iCamIndex);
    set_entvar(iEntCam,var_owner,id);
    set_entvar(iEntCam,var_movetype,MOVETYPE_NOCLIP);
    set_entvar(iEntCam,var_rendermode,kRenderTransColor);
    engset_view(id,iEntCam);
    set_entvar(iEntCam,var_nextthink,get_gametime()+0.01);
    SetThink(iEntCam,"CBaseCam_Think_Post");
    p_iCamId[id] = iEntCam;
    //Костыль для микро;
    client_cmd(id,"stopsound");
}
public Func__RemoveModel(const iEnt) {
    if(!is_nullent(iEnt)) {
        new id = get_entvar(iEnt,var_owner);
        if(is_user_connected(id)) {
            p_iEntityId[id] = 0;
            rg_set_user_invisibility(id,false);
        }
        if(get_entvar(iEnt,var_ent_model) != 0)
            set_entvar(get_entvar(iEnt,var_ent_model),var_flags,FL_KILLME);
        set_entvar(iEnt,var_flags,FL_KILLME);
    }
}
public Func__RemoveCam(const iEnt) {
    if(!is_nullent(iEnt)) {
        new id = get_entvar(iEnt,var_owner);
        if(is_user_connected(id)) {
            p_iCamId[id] = 0;
            engset_view(id,id);
          
            //Костыль для микро;
            client_cmd(id,"stopsound");
        }
        set_entvar(iEnt,var_flags,FL_KILLME);
    }
}
public CBaseModel_Think_Post(const iEnt) {
    if(is_nullent(iEnt)) {
        SetThink(iEnt,"");
        return;
    }
    static id;
    id = get_entvar(iEnt,var_owner);
    if(!is_user_connected(id) || get_entvar(id,var_button) & ATTACK_BTN || !rg_get_user_invisibility(id)) {
        Func__RemoveModel(iEnt);
        SetThink(iEnt,"");
        return;
    }
    static Float:fOriginId[XYZ],Float:fMins[XYZ],Float:fOriginEnt[XYZ];
    get_entvar(id,var_origin,fOriginId);
    get_entvar(iEnt,var_origin,fOriginEnt);
    get_entvar(id,var_mins,fMins);
    fMins[X] = fOriginId[X];
    fMins[Y] = fOriginId[Y];
    fMins[Z] += fOriginId[Z];
    if(!xs_vec_equal(fMins,fOriginEnt)) {
        Func__RemoveModel(iEnt);
        SetThink(iEnt,"");
        client_print(id,print_center,"%L",LANG_PLAYER,"CENTER_DONT_MOVE");
        return;
    }
    set_entvar(iEnt,var_nextthink,get_gametime());
}
public CBaseCam_Think_Post(const iEnt) {
    if(is_nullent(iEnt)) {
        SetThink(iEnt,"");
        return;
    }
    static id;
    id = get_entvar(iEnt,var_owner);
    if(!is_user_connected(id) || !p_iEntityId[id]) {
        Func__RemoveCam(iEnt);
        SetThink(iEnt,"");
        return;
    }
    new Float:flPlayerOrigin[XYZ],Float:flCamOrigin[XYZ],Float:flVecPlayerAngles[XYZ],Float:flVecCamAngles[XYZ];
    get_entvar(id,var_origin,flPlayerOrigin);
    get_entvar(id,var_view_ofs,flVecPlayerAngles);
    flPlayerOrigin[Z] += flVecPlayerAngles[Z];
    get_entvar(id,var_v_angle,flVecPlayerAngles);
    angle_vector(flVecPlayerAngles,ANGLEVECTOR_FORWARD,flVecCamAngles);
    xs_vec_sub_scaled(flPlayerOrigin,flVecCamAngles,float(g_pCvarNum__CamDistance), flCamOrigin);
    engfunc(EngFunc_TraceLine,flPlayerOrigin,flCamOrigin,IGNORE_MONSTERS,id,0);
    new Float:flFraction;
    get_tr2(0,TR_flFraction,flFraction);
    xs_vec_sub_scaled(flPlayerOrigin,flVecCamAngles,flFraction * float(g_pCvarNum__CamDistance),flCamOrigin);
    set_entvar(iEnt,var_origin,flCamOrigin);
    set_entvar(iEnt,var_angles,flVecPlayerAngles);
    set_entvar(iEnt,var_nextthink,get_gametime()+0.01);
}
public client_putinserver(id) {
    p_iEntityId[id] = p_iCamId[id] = 0;
}
public Func__ReadSettingsFile() {
    bind_pcvar_string(
        create_cvar(
            .name = "dance_flag_access",
            .string = "a",
            .description = "Флаг доступа к меню"
        ),
        g_pCvarrString__FlagAccess,charsmax(g_pCvarrString__FlagAccess)
    );
    bind_pcvar_string(
        create_cvar(
            .name = "dance_clcmd_open_menu",
            .string = "dance",
            .description = "Команда для открытия меню^nРегистрируется во всех чатах"
        ),
        g_pCvarString__ClCmdOpenMenu,charsmax(g_pCvarString__ClCmdOpenMenu)
    );
    bind_pcvar_num(
        create_cvar(
            .name = "dance_cam_distance",
            .string = "150",
            .description = "Расстояние камеры от игрока"
        ),
        g_pCvarNum__CamDistance
    );
    bind_pcvar_float(
        create_cvar(
            .name = "dance_flood",
            .string = "1.0",
            .description = "Через сколько секунд можно выбрать новый танец"
        ),
        g_pCvarFloat__Flood
    );
    AutoExecConfig(true,"Dance");
    new szData[256];
    formatex(szData,charsmax(szData),"addons/amxmodx/data/lang/Dance.txt");
  
    if(!file_exists(szData))
        write_file(szData,
            "[ru]^n^n\
            DANCE_MENU_NAME = Выбор движения^n^n\
            CENTER_ONLY_ADMINS = Только для админов!^n\
            CENTER_ONLY_ALIVE = Только для живых игроков!^n\
            CENTER_OFF_GROUND = Вы должны быть на земле!^n\
            CENTER_DONT_FLOOD = Не спамь!^n\
            CENTER_DONT_MOVE = Не двигайся!"
        );
    formatex(szData,charsmax(szData),"addons/amxmodx/configs/Dance.ini");
    if(!file_exists(szData))
        write_file(szData,
            ";Название секции где будет пункт | имя в меню | путь до моедил | анимация | скорость анимации | флаг доступа^n\
            ;Если требуется добавить в основное меню, в названии секции указать '_'^n\
            ;Если флаг доступа не требуется указать '_'"
        );
  
    g_Array__Dance = ArrayCreate(ArrayData);
    g_Array__MenuIndex = ArrayCreate(256);
    new f = fopen(szData,"r");
    new aData[ArrayData];
    new szFileData[6][256];
    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[])) == 6) {
            copy(aData[MENU_INDEX],charsmax(aData),szFileData[0]);
            copy(aData[MENU_NAME],charsmax(aData),szFileData[1]);
            copy(aData[MODEL_WAY],charsmax(aData),szFileData[2]);
            aData[SEQUENCE] = str_to_num(szFileData[3]);
            aData[FRAMERATE] = str_to_float(szFileData[4]);
          
            aData[FLAG_ACCESS] = strcmp(szFileData[5],"_") != 0 ? read_flags(szFileData[5]) : '0';
            ArrayPushArray(g_Array__Dance,aData);
        }
        else
            log_amx("[DanceMenu] Файл [Dance.ini] заполнен не верно! Строка: %i",iLine);
        continue;
    }
    fclose(f);
}
public plugin_natives() {
    register_native("nd_get_active_dance","native_nd_get_active_dance");
    register_native("nd_remove_dance","native_nd_remove_dance");
}
public bool:native_nd_get_active_dance(iPlugin,iParam) {
    return bool:(p_iEntityId[get_param(1)] != 0);
}
public native_nd_remove_dance(iPlugin,iParam) {
    new id = get_param(1);
    if(p_iEntityId[id]) {
        Func__RemoveModel(p_iEntityId[id]);
        Func__RemoveCam(p_iCamId[id]);
    }
}
stock bool:UTIL__IsCreateDanceEntity(const id) {
    if(!is_user_alive(id)) {
        client_print(id,print_center,"%L",LANG_PLAYER,"CENTER_ONLY_ALIVE");
        return false;
    }
    if(!(get_entvar(id,var_flags) & FL_ONGROUND) || get_entvar(id,var_waterlevel) != 0) {
        client_print(id,print_center,"%L",LANG_PLAYER,"CENTER_OFF_GROUND");
        return false;
    }
    if((p_fFloodDanceMenu[id]-get_gametime()) > 0) {
        client_print(id,print_center,"%L",LANG_PLAYER,"CENTER_DONT_FLOOD");
        p_fFloodDanceMenu[id] = get_gametime()+g_pCvarFloat__Flood;
        return false;
    }
    return true;
}
stock rg_set_user_invisibility(const id, bool:bToggle = true) {
    new iEffects = get_entvar(id,var_effects);
    set_entvar(id,var_effects,bToggle ? (iEffects |= EF_NODRAW) : (iEffects &= ~EF_NODRAW))
}
stock bool:rg_get_user_invisibility(const id) {
    return bool:(get_entvar(id, var_effects) & EF_NODRAW);
}
stock UTIL__RegisterClCmd(const szCmd[],const szFunc[]) {
    register_clcmd(fmt("%s",szCmd),szFunc);
    register_clcmd(fmt("say /%s",szCmd),szFunc);
    register_clcmd(fmt("say_team /%s",szCmd),szFunc);
}
stock UTIL__DisplayMenu(const id,const iMenu,const szExitName[] = "Выход") {
    menu_setprop(iMenu,MPROP_NEXTNAME,"Далее");
    menu_setprop(iMenu,MPROP_BACKNAME,"Назад");
    menu_setprop(iMenu,MPROP_EXITNAME,szExitName);
    menu_setprop(iMenu,MPROP_NUMBER_COLOR,"\y");
    if(is_user_connected(id))
        menu_display(id,iMenu);
    else
        menu_destroy(iMenu);
}
Настройки
Код:
// This file was auto-generated by AMX Mod X (v1.10.0.5402)
// Cvars for plugin "unknown" by "unknown" (NewDenceMenu.amxx, vunknown)

// Флаг доступа к меню
// -
// Default: "a"
dence_flag_access ""
// Команда для открытия меню
// Регистрируется во всех чатах
// -
// Default: "dance"
dence_clcmd_open_menu "dance"
// Расстояние камеры от игрока
// -
// Default: "150"
dence_cam_distance "150"
// Через сколько секунд можно выбрать новый танец
// -
// Default: "1.0"
dence_flood "1.0"

Код:
;Название секции где будет пункт | имя в меню | путь до модели | анимация | скорость анимации | флаг доступа
;Если требуется добавить в основное меню, в названии секции указать '_'
;Если флаг доступа не требуется указать '_'

Заложники | Заложник 1 | models/animations_fix.mdl | 16 | 1.0 | _
Заложники | Заложник 2 | models/animations_fix.mdl | 17 | 1.0 | _
Заложники | Заложник 3 | models/animations_fix.mdl | 21 | 1.0 | _

Танцы | Ква-ква | models/animations_fix.mdl | 0 | 1.0 | _
Танцы | Пьяная походка | models/animations_fix.mdl | 1 | 1.0 | _
Танцы | Робота | models/animations_fix.mdl | 2 | 1.0 | _

_ | Отжимания | models/animations_fix.mdl | 8 | 1.0 | _
_ | Медитация | models/animations_fix.mdl | 7 | 1.0 | _
Интерфейсы (API)
//Вернёт true если есть активная анимация;
//В противном случае false;
native nd_get_active_dance(const pPlayer);

//Сбросит активную анимацию, если таковая имеется;
native nd_remove_dance(const pPlayer);
Сверху Снизу