/*
Максимально прокаченный парашют с огромными возможностями и гибкими настройками.
Автор: MaXaoH
*/
#include <amxmodx>
#include <fakemeta>
#include <engine>
#include <fun>
#include <hamsandwich>
#include <nvault>
#include <cstrike>
#pragma semicolon 1
#define PLUGIN "Ultimate Parachute"
#define VERSION "1.0"
#define AUTHOR "MaXaoH"
#define cm(%0) ( sizeof(%0) - 1 )
const XO_PLAYER = 5;
const m_flFallVelocity = 251;
#define MAX_SPEEDS 32
#define MAX_MODELS 64
#define MAX_TRAILS 64
#define MAX_TRAIL_COLORS 32
#define MAX_TRAIL_LENGTHS 32
enum
{
DEPLOY_HOLD = 0,
DEPLOY_TAP = 1,
DEPLOY_AUTO = 2
};
new const g_szDeployModeName[3][] = { "Удержание", "Нажатие (повторно - закрыть)", "Автоматически (нажатие - закрыть)" };
new const PARA_CLASS[] = "parachute";
#define TE_BEAMFOLLOW 22
#define TE_KILLBEAM 99
new g_szCfgPath[256];
new g_szAccessFlags[32];
new g_iAccessBits;
new g_iAccessTeamTT;
new g_iAccessTeamCT;
new g_iWeaponUsage;
new g_iDeployModeAccess;
new g_iDeployModeDefault;
new g_iFallSpeedAccess;
new g_szFallSpeedDefault[32];
new g_iThirdAccess;
new g_iThirdDefault;
new g_iModelAccess;
new g_szModelDefault[128];
new Float:g_flAutoThreshold;
new g_iDeploysPerRound;
new g_szDeploySound[128];
new g_iSpeedsNum;
new g_szSpeedName[MAX_SPEEDS][32];
new Float:g_flSpeedValue[MAX_SPEEDS];
new g_iModelsNum;
new g_szModelPath[MAX_MODELS][128];
new g_szModelName[MAX_MODELS][48];
new g_iTrailAccess;
new g_szTrailDefault[128];
new g_iTrailColorAccess;
new g_szTrailColorDefault[32];
new g_iTrailLengthAccess;
new g_szTrailLengthDefault[16];
new g_iTrailsNum;
new g_szTrailPath[MAX_TRAILS][128];
new g_szTrailName[MAX_TRAILS][48];
new g_iTrailSpriteIdx[MAX_TRAILS];
new g_iTrailColorsNum;
new g_szTrailColorName[MAX_TRAIL_COLORS][48];
new g_iTrailColorRGB[MAX_TRAIL_COLORS][3];
new g_iTrailLengthsNum;
new g_szTrailLengthName[MAX_TRAIL_LENGTHS][48];
new g_iTrailLengthVal[MAX_TRAIL_LENGTHS];
new g_iDefaultTrailIndex;
new g_iDefaultTrailColorIndex;
new g_iDefaultTrailLengthIndex;
new g_iTrailWidth = 10;
new g_iTrailIndex[33];
new g_iTrailColorIndex[33];
new g_iTrailLengthIndex[33];
new g_iTrailMenuPage[33];
new g_iTrailColorMenuPage[33];
new g_iTrailLengthMenuPage[33];
new bool:g_bTrailActive[33];
new g_iDefaultSpeedIndex;
new g_iDefaultModelIndex;
new g_iVault;
new g_iParaEnt[33];
new bool:g_bParaDeployed[33];
new g_iDeployMode[33];
new g_iSpeedIndex[33];
new bool:g_bThird[33];
new g_iModelIndex[33];
new g_iModelMenuPage[33];
new g_iLastWeapon[33];
new g_iDeploysUsed[33];
public plugin_precache()
{
precache_model("models/rpgrocket.mdl");
BuildConfigPath();
LoadConfig(true);
}
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR);
register_clcmd("say /parachute", "Cmd_Menu");
register_clcmd("say_team /parachute", "Cmd_Menu");
RegisterHam(Ham_Player_PreThink, "player", "OnPreThink", 0);
RegisterHam(Ham_Spawn, "player", "OnSpawnPost", 1);
RegisterHam(Ham_Killed, "player", "OnKilledPost", 1);
register_logevent("OnRoundStart", 2, "1=Round_Start");
}
public plugin_cfg()
{
BuildConfigPath();
LoadConfig(false);
g_iVault = nvault_open("ultimate_parachute");
if (g_iVault == INVALID_HANDLE)
set_fail_state("Error opening nVault (ultimate_parachute)");
}
public plugin_end()
{
if (g_iVault != INVALID_HANDLE)
nvault_close(g_iVault);
}
public OnSpawnPost(id)
{
if(!is_user_connected(id)) return;
RemoveParachute(id, true);
g_iDeploysUsed[id] = 0;
LoadPlayerData(id);
}
public OnKilledPost(victim, attacker, shouldgib)
{
if(!is_user_connected(victim)) return;
RemoveParachute(victim, true);
}
public OnRoundStart()
{
for(new id=1; id<=32; id++)
{
g_iDeploysUsed[id] = 0;
if(is_user_connected(id))
RemoveParachute(id, true);
}
}
public client_putinserver(id)
{
ResetPlayerState(id);
}
public client_disconnected(id)
{
SavePlayerData(id);
ResetPlayerState(id);
}
public OnPreThink(id)
{
if(!is_user_alive(id))
return;
if(!HasParachuteAccess(id))
{
RemoveParachute(id, false);
return;
}
if(pev(id, pev_movetype) == MOVETYPE_FLY)
{
RemoveParachute(id, false);
return;
}
new buttons = pev(id, pev_button);
new oldbuttons = pev(id, pev_oldbuttons);
new flags = pev(id, pev_flags);
static Float:vel[3];
pev(id, pev_velocity, vel);
new bool:bDescending = (vel[2] < 0.0);
switch(g_iDeployMode[id])
{
case DEPLOY_HOLD:
{
if( (buttons & IN_USE) && !(flags & FL_ONGROUND) && bDescending )
{
if(!g_bParaDeployed[id])
{
if(!TryStartDeploy(id))
{
RemoveParachute(id, false);
return;
}
g_bParaDeployed[id] = true;
StartTrailIfNeeded(id);
}
EnsureEntity(id);
ApplyParachute(id, vel);
}
else
{
RemoveParachute(id, false);
}
}
case DEPLOY_TAP:
{
if( (buttons & IN_USE) && !(oldbuttons & IN_USE) && !(flags & FL_ONGROUND) && bDescending )
{
if(!g_bParaDeployed[id])
{
if(!TryStartDeploy(id))
return;
EnsureEntity(id);
g_bParaDeployed[id] = true;
StartTrailIfNeeded(id);
}
else
{
RemoveParachute(id, false);
}
}
if(g_bParaDeployed[id] && !(flags & FL_ONGROUND) && bDescending)
{
EnsureEntity(id);
ApplyParachute(id, vel);
}
else if(flags & FL_ONGROUND)
{
RemoveParachute(id, false);
}
}
case DEPLOY_AUTO:
{
static Float:fallvel;
fallvel = get_pdata_float(id, m_flFallVelocity, XO_PLAYER);
if(!g_bParaDeployed[id] && bDescending && fallvel >= g_flAutoThreshold && !(flags & FL_ONGROUND))
{
if(!TryStartDeploy(id))
return;
EnsureEntity(id);
g_bParaDeployed[id] = true;
StartTrailIfNeeded(id);
}
if(g_bParaDeployed[id] && !(flags & FL_ONGROUND) && bDescending)
{
EnsureEntity(id);
ApplyParachute(id, vel);
}
else if(flags & FL_ONGROUND)
{
RemoveParachute(id, false);
}
if(g_bParaDeployed[id] && (buttons & IN_USE) && !(oldbuttons & IN_USE) && !(flags & FL_ONGROUND))
{
RemoveParachute(id, false);
}
}
}
if(!(buttons & IN_USE) && (oldbuttons & IN_USE))
{
RestoreGravityIfNeeded(id);
}
}
public Cmd_Menu(id)
{
if(!is_user_connected(id)) return PLUGIN_HANDLED;
if(!HasParachuteAccess(id))
{
client_print(id, print_chat, "[Ultimate Parachute] У вас нет доступа к парашюту.");
return PLUGIN_HANDLED;
}
if(g_bParaDeployed[id] || g_iParaEnt[id] > 0)
return PLUGIN_HANDLED;
new menu = menu_create("Настройки парашюта", "Menu_Handler");
new sz[192];
if(g_iDeployModeAccess)
{
formatex(sz, cm(sz), "Режим раскрытия: \y%s", g_szDeployModeName[g_iDeployMode[id]]);
menu_additem(menu, sz, "0");
}
else
{
formatex(sz, cm(sz), "Режим раскрытия: \d%s", g_szDeployModeName[g_iDeployMode[id]]);
menu_additem(menu, sz, "0");
}
if(g_iThirdAccess)
{
formatex(sz, cm(sz), "Вид: \y%s", g_bThird[id] ? "от 3 лица" : "от 1 лица");
menu_additem(menu, sz, "1");
}
else
{
formatex(sz, cm(sz), "Вид: \d%s", g_bThird[id] ? "от 3 лица" : "от 1 лица");
menu_additem(menu, sz, "1");
}
if(g_iFallSpeedAccess && g_iSpeedsNum > 0)
{
formatex(sz, cm(sz), "Скорость падения: \y%s", g_szSpeedName[g_iSpeedIndex[id]]);
menu_additem(menu, sz, "2");
}
else
{
formatex(sz, cm(sz), "Скорость падения: \d%s", (g_iSpeedsNum > 0) ? g_szSpeedName[g_iSpeedIndex[id]] : "нет списка");
menu_additem(menu, sz, "2");
}
if(g_iModelsNum > 0)
{
new const szRandom[] = "Меняющаяся модель";
if(g_iModelIndex[id] == -1)
formatex(sz, cm(sz), "Модель: \y%s", szRandom);
else
formatex(sz, cm(sz), "Модель: \y%s", g_szModelName[g_iModelIndex[id]]);
if(!g_iModelAccess)
replace(sz, cm(sz), "\y", "\d");
menu_additem(menu, sz, "3");
}
else
{
formatex(sz, cm(sz), "Модель: \dнет списка");
menu_additem(menu, sz, "3");
}
if(g_iTrailsNum > 0)
{
new const szRandomTrail[] = "Меняющийся хвост";
if(g_iTrailIndex[id] == -1)
formatex(sz, cm(sz), "Хвост: \y%s", szRandomTrail);
else
formatex(sz, cm(sz), "Хвост: \y%s", g_szTrailName[g_iTrailIndex[id]]);
if(!g_iTrailAccess)
replace(sz, cm(sz), "\y", "\d");
menu_additem(menu, sz, "4");
}
else
{
formatex(sz, cm(sz), "Хвост: \dнет списка");
menu_additem(menu, sz, "4");
}
if(g_iTrailColorsNum > 0)
{
new const szRandomColor[] = "Случайный";
if(g_iTrailColorIndex[id] == -1)
formatex(sz, cm(sz), "Цвет хвоста: \y%s", szRandomColor);
else
formatex(sz, cm(sz), "Цвет хвоста: \y%s", g_szTrailColorName[g_iTrailColorIndex[id]]);
if(!g_iTrailColorAccess || !g_iTrailAccess)
replace(sz, cm(sz), "\y", "\d");
menu_additem(menu, sz, "5");
}
else
{
formatex(sz, cm(sz), "Цвет хвоста: \dнет списка");
menu_additem(menu, sz, "5");
}
if(g_iTrailLengthsNum > 0)
{
new const szRandomLen[] = "Случайная";
if(g_iTrailLengthIndex[id] == -1)
formatex(sz, cm(sz), "Длина хвоста: \y%s", szRandomLen);
else
formatex(sz, cm(sz), "Длина хвоста: \y%s", g_szTrailLengthName[g_iTrailLengthIndex[id]]);
if(!g_iTrailLengthAccess || !g_iTrailAccess)
replace(sz, cm(sz), "\y", "\d");
menu_additem(menu, sz, "6");
}
else
{
formatex(sz, cm(sz), "Длина хвоста: \dнет списка");
menu_additem(menu, sz, "6");
}
menu_setprop(menu, MPROP_EXIT, MEXIT_ALL);
menu_display(id, menu);
return PLUGIN_HANDLED;
}
public Menu_Handler(id, menu, item)
{
if(item == MENU_EXIT)
{
menu_destroy(menu);
SavePlayerData(id);
return PLUGIN_HANDLED;
}
new info[8], access, callback;
menu_item_getinfo(menu, item, access, info, cm(info), _, _, callback);
new key = str_to_num(info);
switch(key)
{
case 0:
{
if(g_iDeployModeAccess)
{
g_iDeployMode[id]++;
if(g_iDeployMode[id] > DEPLOY_AUTO) g_iDeployMode[id] = DEPLOY_HOLD;
}
}
case 1:
{
if(g_iThirdAccess)
{
g_bThird[id] = !g_bThird[id];
if(!g_bThird[id]) set_view(id, CAMERA_NONE);
}
}
case 2:
{
if(g_iFallSpeedAccess && g_iSpeedsNum > 0)
{
g_iSpeedIndex[id]++;
if(g_iSpeedIndex[id] >= g_iSpeedsNum) g_iSpeedIndex[id] = 0;
}
}
case 3:
{
if(g_iModelAccess && g_iModelsNum > 0)
{
menu_destroy(menu);
ShowModelMenu(id, 0);
return PLUGIN_HANDLED;
}
}
case 4:
{
if(g_iTrailAccess && g_iTrailsNum > 0)
{
menu_destroy(menu);
ShowTrailMenu(id, 0);
return PLUGIN_HANDLED;
}
}
case 5:
{
if(g_iTrailAccess && g_iTrailColorAccess && g_iTrailColorsNum > 0)
{
menu_destroy(menu);
ShowTrailColorMenu(id, 0);
return PLUGIN_HANDLED;
}
}
case 6:
{
if(g_iTrailAccess && g_iTrailLengthAccess && g_iTrailLengthsNum > 0)
{
menu_destroy(menu);
ShowTrailLengthMenu(id, 0);
return PLUGIN_HANDLED;
}
}
}
menu_destroy(menu);
Cmd_Menu(id);
SavePlayerData(id);
return PLUGIN_HANDLED;
}
stock ShowModelMenu(id, page)
{
if(!is_user_connected(id)) return;
new const perPage = 7;
new totalItems = g_iModelsNum + 1;
new maxPage = 0;
if(totalItems > 0)
maxPage = (totalItems - 1) / perPage;
if(page < 0) page = 0;
if(page > maxPage) page = maxPage;
g_iModelMenuPage[id] = page;
new menu = menu_create("Выбор модели", "ModelMenu_Handler");
menu_additem(menu, "Меняющаяся модель", "R");
new sz[96], info[12];
for(new i = 0; i < g_iModelsNum; i++)
{
formatex(sz, charsmax(sz), "%s", g_szModelName);
formatex(info, charsmax(info), "%d", i);
menu_additem(menu, sz, info);
}
menu_setprop(menu, MPROP_PERPAGE, perPage);
menu_setprop(menu, MPROP_BACKNAME, "Назад");
menu_setprop(menu, MPROP_NEXTNAME, "Вперед");
menu_setprop(menu, MPROP_EXITNAME, "Выход");
menu_display(id, menu, page);
}
public ModelMenu_Handler(id, menu, item)
{
if(item == MENU_EXIT)
{
menu_destroy(menu);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
if(item == MENU_BACK)
{
menu_destroy(menu);
ShowModelMenu(id, g_iModelMenuPage[id] - 1);
return PLUGIN_HANDLED;
}
if(item == MENU_MORE)
{
menu_destroy(menu);
ShowModelMenu(id, g_iModelMenuPage[id] + 1);
return PLUGIN_HANDLED;
}
new info[12], access, callback;
menu_item_getinfo(menu, item, access, info, charsmax(info), _, _, callback);
if(info[0] == 'R')
{
g_iModelIndex[id] = -1;
}
else
{
new idx = str_to_num(info);
if(idx >= 0 && idx < g_iModelsNum)
g_iModelIndex[id] = idx;
}
menu_destroy(menu);
SavePlayerData(id);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
stock bool:TryStartDeploy(id)
{
if(g_iDeploysPerRound > 0 && g_iDeploysUsed[id] >= g_iDeploysPerRound)
return false;
g_iDeploysUsed[id]++;
if(g_szDeploySound[0])
emit_sound(id, CHAN_ITEM, g_szDeploySound, 1.0, ATTN_NORM, 0, PITCH_NORM);
if(!g_iWeaponUsage)
DenyWeapons(id);
if(g_bThird[id])
set_view(id, CAMERA_3RDPERSON);
set_pev(id, pev_gravity, 0.1);
return true;
}
stock EnsureEntity(id)
{
if(g_iParaEnt[id] > 0 && is_valid_ent(g_iParaEnt[id]))
return;
new ent = create_entity("info_target");
if(ent <= 0) return;
entity_set_string(ent, EV_SZ_classname, PARA_CLASS);
if(g_iModelsNum > 0)
{
new mdlIndex = g_iModelIndex[id];
if(mdlIndex < 0) mdlIndex = random_num(0, g_iModelsNum - 1);
entity_set_model(ent, g_szModelPath[mdlIndex]);
}
entity_set_int(ent, EV_INT_movetype, MOVETYPE_FOLLOW);
entity_set_int(ent, EV_INT_solid, SOLID_NOT);
entity_set_edict(ent, EV_ENT_aiment, id);
entity_set_edict(ent, EV_ENT_owner, id);
entity_set_int(ent, EV_INT_sequence, 0);
entity_set_int(ent, EV_INT_gaitsequence, 1);
entity_set_float(ent, EV_FL_frame, 0.0);
entity_set_float(ent, EV_FL_fuser1, 0.0);
g_iParaEnt[id] = ent;
}
stock ShowTrailMenu(id, page)
{
if(!is_user_connected(id)) return;
new const perPage = 7;
new totalItems = g_iTrailsNum + 1;
new maxPage = 0;
if(totalItems > 0)
maxPage = (totalItems - 1) / perPage;
if(page < 0) page = 0;
if(page > maxPage) page = maxPage;
g_iTrailMenuPage[id] = page;
new menu = menu_create("Выбор хвоста", "TrailMenu_Handler");
menu_additem(menu, "Меняющийся хвост", "R");
new sz[96], info[12];
for(new i = 0; i < g_iTrailsNum; i++)
{
formatex(sz, charsmax(sz), "%s", g_szTrailName);
formatex(info, charsmax(info), "%d", i);
menu_additem(menu, sz, info);
}
menu_setprop(menu, MPROP_PERPAGE, perPage);
menu_setprop(menu, MPROP_BACKNAME, "Назад");
menu_setprop(menu, MPROP_NEXTNAME, "Вперед");
menu_setprop(menu, MPROP_EXITNAME, "Выход");
menu_display(id, menu, page);
}
public TrailMenu_Handler(id, menu, item)
{
if(item == MENU_EXIT)
{
menu_destroy(menu);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
if(item == MENU_BACK)
{
menu_destroy(menu);
ShowTrailMenu(id, g_iTrailMenuPage[id] - 1);
return PLUGIN_HANDLED;
}
if(item == MENU_MORE)
{
menu_destroy(menu);
ShowTrailMenu(id, g_iTrailMenuPage[id] + 1);
return PLUGIN_HANDLED;
}
new info[12], access, callback;
menu_item_getinfo(menu, item, access, info, charsmax(info), _, _, callback);
if(info[0] == 'R')
{
g_iTrailIndex[id] = -1;
}
else
{
new idx = str_to_num(info);
if(idx >= 0 && idx < g_iTrailsNum)
g_iTrailIndex[id] = idx;
}
menu_destroy(menu);
SavePlayerData(id);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
stock ShowTrailColorMenu(id, page)
{
if(!is_user_connected(id)) return;
new const perPage = 7;
new totalItems = g_iTrailColorsNum + 1;
new maxPage = 0;
if(totalItems > 0)
maxPage = (totalItems - 1) / perPage;
if(page < 0) page = 0;
if(page > maxPage) page = maxPage;
g_iTrailColorMenuPage[id] = page;
new menu = menu_create("Выбор цвета хвоста", "TrailColorMenu_Handler");
menu_additem(menu, "Случайный", "R");
new sz[96], info[12];
for(new i = 0; i < g_iTrailColorsNum; i++)
{
formatex(sz, charsmax(sz), "%s", g_szTrailColorName);
formatex(info, charsmax(info), "%d", i);
menu_additem(menu, sz, info);
}
menu_setprop(menu, MPROP_PERPAGE, perPage);
menu_setprop(menu, MPROP_BACKNAME, "Назад");
menu_setprop(menu, MPROP_NEXTNAME, "Вперед");
menu_setprop(menu, MPROP_EXITNAME, "Выход");
menu_display(id, menu, page);
}
public TrailColorMenu_Handler(id, menu, item)
{
if(item == MENU_EXIT)
{
menu_destroy(menu);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
if(item == MENU_BACK)
{
menu_destroy(menu);
ShowTrailColorMenu(id, g_iTrailColorMenuPage[id] - 1);
return PLUGIN_HANDLED;
}
if(item == MENU_MORE)
{
menu_destroy(menu);
ShowTrailColorMenu(id, g_iTrailColorMenuPage[id] + 1);
return PLUGIN_HANDLED;
}
new info[12], access, callback;
menu_item_getinfo(menu, item, access, info, charsmax(info), _, _, callback);
if(info[0] == 'R')
{
g_iTrailColorIndex[id] = -1;
}
else
{
new idx = str_to_num(info);
if(idx >= 0 && idx < g_iTrailColorsNum)
g_iTrailColorIndex[id] = idx;
}
menu_destroy(menu);
SavePlayerData(id);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
stock ShowTrailLengthMenu(id, page)
{
if(!is_user_connected(id)) return;
new const perPage = 7;
new totalItems = g_iTrailLengthsNum + 1;
new maxPage = 0;
if(totalItems > 0)
maxPage = (totalItems - 1) / perPage;
if(page < 0) page = 0;
if(page > maxPage) page = maxPage;
g_iTrailLengthMenuPage[id] = page;
new menu = menu_create("Выбор длины хвоста", "TrailLengthMenu_Handler");
menu_additem(menu, "Случайная", "R");
new sz[96], info[12];
for(new i = 0; i < g_iTrailLengthsNum; i++)
{
formatex(sz, charsmax(sz), "%s", g_szTrailLengthName);
formatex(info, charsmax(info), "%d", i);
menu_additem(menu, sz, info);
}
menu_setprop(menu, MPROP_PERPAGE, perPage);
menu_setprop(menu, MPROP_BACKNAME, "Назад");
menu_setprop(menu, MPROP_NEXTNAME, "Вперед");
menu_setprop(menu, MPROP_EXITNAME, "Выход");
menu_display(id, menu, page);
}
public TrailLengthMenu_Handler(id, menu, item)
{
if(item == MENU_EXIT)
{
menu_destroy(menu);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
if(item == MENU_BACK)
{
menu_destroy(menu);
ShowTrailLengthMenu(id, g_iTrailLengthMenuPage[id] - 1);
return PLUGIN_HANDLED;
}
if(item == MENU_MORE)
{
menu_destroy(menu);
ShowTrailLengthMenu(id, g_iTrailLengthMenuPage[id] + 1);
return PLUGIN_HANDLED;
}
new info[12], access, callback;
menu_item_getinfo(menu, item, access, info, charsmax(info), _, _, callback);
if(info[0] == 'R')
{
g_iTrailLengthIndex[id] = -1;
}
else
{
new idx = str_to_num(info);
if(idx >= 0 && idx < g_iTrailLengthsNum)
g_iTrailLengthIndex[id] = idx;
}
menu_destroy(menu);
SavePlayerData(id);
Cmd_Menu(id);
return PLUGIN_HANDLED;
}
stock ApplyParachute(id, Float:vel[3])
{
set_pev(id, pev_gravity, 0.1);
if(g_iSpeedsNum > 0)
{
new i = g_iSpeedIndex[id];
new Float:target = g_flSpeedValue;
vel[2] = (vel[2] + 40.0 < target) ? (vel[2] + 40.0) : target;
set_pev(id, pev_velocity, vel);
}
new ent = g_iParaEnt[id];
if(ent > 0 && is_valid_ent(ent))
{
new Float:frame;
if(entity_get_int(ent, EV_INT_sequence) == 0)
{
frame = entity_get_float(ent, EV_FL_fuser1) + 1.0;
entity_set_float(ent, EV_FL_fuser1, frame);
entity_set_float(ent, EV_FL_frame, frame);
if(frame > 100.0)
{
entity_set_float(ent, EV_FL_animtime, 0.0);
entity_set_float(ent, EV_FL_framerate, 0.4);
entity_set_int(ent, EV_INT_sequence, 1);
entity_set_int(ent, EV_INT_gaitsequence, 1);
entity_set_float(ent, EV_FL_frame, 0.0);
entity_set_float(ent, EV_FL_fuser1, 0.0);
}
}
}
}
stock RemoveParachute(id, bool:bForce)
{
if(g_iParaEnt[id] > 0)
{
if(is_valid_ent(g_iParaEnt[id]))
remove_entity(g_iParaEnt[id]);
g_iParaEnt[id] = 0;
}
if(g_bParaDeployed[id] || bForce)
{
g_bParaDeployed[id] = false;
KillTrail(id);
set_view(id, CAMERA_NONE);
if(!g_iWeaponUsage)
RestoreWeapon(id);
RestoreGravityIfNeeded(id);
}
}
stock RestoreGravityIfNeeded(id)
{
if(!is_user_alive(id)) return;
static Float:gr;
pev(id, pev_gravity, gr);
if(gr == 0.1)
set_pev(id, pev_gravity, 1.0);
}
stock KillTrail(id)
{
if(!g_bTrailActive[id])
return;
message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
write_byte(TE_KILLBEAM);
write_short(id);
message_end();
g_bTrailActive[id] = false;
}
stock StartTrailIfNeeded(id)
{
if(!g_bParaDeployed[id])
return;
if(!g_iTrailAccess || g_iTrailsNum <= 0 || g_iTrailLengthsNum <= 0)
return;
new iLenIdx = g_iTrailLengthIndex[id];
if(iLenIdx == -1 && g_iTrailLengthsNum > 0)
iLenIdx = random_num(0, g_iTrailLengthsNum - 1);
if(iLenIdx < 0 || iLenIdx >= g_iTrailLengthsNum)
return;
new life = g_iTrailLengthVal[iLenIdx];
if(life <= 0)
{
KillTrail(id);
return;
}
new iTrailIdx = g_iTrailIndex[id];
if(iTrailIdx == -1 && g_iTrailsNum > 0)
iTrailIdx = random_num(0, g_iTrailsNum - 1);
if(iTrailIdx < 0 || iTrailIdx >= g_iTrailsNum)
return;
new sprite = g_iTrailSpriteIdx[iTrailIdx];
if(sprite <= 0)
return;
new iColorIdx = g_iTrailColorIndex[id];
if(iColorIdx == -1 && g_iTrailColorsNum > 0)
iColorIdx = random_num(0, g_iTrailColorsNum - 1);
new r = 255, g = 255, b = 255;
if(iColorIdx >= 0 && iColorIdx < g_iTrailColorsNum)
{
r = g_iTrailColorRGB[iColorIdx][0];
g = g_iTrailColorRGB[iColorIdx][1];
b = g_iTrailColorRGB[iColorIdx][2];
}
KillTrail(id);
message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
write_byte(TE_BEAMFOLLOW);
write_short(id);
write_short(sprite);
write_byte(clamp(life, 0, 255));
write_byte(g_iTrailWidth);
write_byte(r);
write_byte(g);
write_byte(b);
write_byte(255);
message_end();
g_bTrailActive[id] = true;
}
stock DenyWeapons(id)
{
new weapon = get_user_weapon(id);
if(weapon != CSW_KNIFE)
{
g_iLastWeapon[id] = weapon;
engclient_cmd(id, "weapon_knife");
}
new buttons = pev(id, pev_button);
buttons &= ~IN_ATTACK;
buttons &= ~IN_ATTACK2;
set_pev(id, pev_button, buttons);
}
stock RestoreWeapon(id)
{
if(g_iLastWeapon[id] <= 0) return;
new szWpn[32];
get_weaponname(g_iLastWeapon[id], szWpn, cm(szWpn));
engclient_cmd(id, szWpn);
g_iTrailIndex[id] = (g_iTrailsNum > 0) ? g_iDefaultTrailIndex : 0;
g_iTrailColorIndex[id] = (g_iTrailColorsNum > 0) ? g_iDefaultTrailColorIndex : 0;
g_iTrailLengthIndex[id] = (g_iTrailLengthsNum > 0) ? g_iDefaultTrailLengthIndex : 0;
g_bTrailActive[id] = false;
g_iLastWeapon[id] = 0;
}
stock BuildConfigPath()
{
new cfgdir[128];
get_localinfo("amxx_configsdir", cfgdir, cm(cfgdir));
formatex(g_szCfgPath, cm(g_szCfgPath), "%s/ultimate_parachute.ini", cfgdir);
}
stock LoadConfig(bool:bPrecacheOnly)
{
g_szAccessFlags[0] = 0;
g_iAccessBits = 0;
g_iAccessTeamTT = 1;
g_iAccessTeamCT = 1;
g_iWeaponUsage = 1;
g_iDeployModeAccess = 1;
g_iDeployModeDefault = DEPLOY_HOLD;
g_flAutoThreshold = 350.0;
g_iDeploysPerRound = 0;
g_iFallSpeedAccess = 1;
g_szFallSpeedDefault[0] = 0;
g_iThirdAccess = 1;
g_iThirdDefault = 0;
g_iModelAccess = 1;
g_szModelDefault[0] = 0;
g_szDeploySound[0] = 0;
g_iSpeedsNum = 0;
g_iModelsNum = 0;
g_iTrailAccess = 1;
g_szTrailDefault[0] = 0;
g_iTrailColorAccess = 1;
g_szTrailColorDefault[0] = 0;
g_iTrailLengthAccess = 1;
g_szTrailLengthDefault[0] = 0;
g_iTrailsNum = 0;
g_iTrailColorsNum = 0;
g_iTrailLengthsNum = 0;
g_iDefaultSpeedIndex = 0;
g_iDefaultModelIndex = 0;
g_iDefaultTrailIndex = -1;
g_iDefaultTrailColorIndex = -1;
g_iDefaultTrailLengthIndex = 0;
if(!file_exists(g_szCfgPath))
{
return;
}
new line[256], section[32];
section[0] = 0;
new fp = fopen(g_szCfgPath, "rt");
if(!fp) return;
while(!feof(fp))
{
fgets(fp, line, cm(line));
trim(line);
if(!line[0]) continue;
if(line[0] == ';') continue;
if(line[0] == '/' && line[1] == '/') continue;
if(line[0] == '[')
{
new iEnd = contain(line, "]");
if(iEnd > 1)
{
new j = 0;
for(new i = 1; i < iEnd && j < cm(section); i++)
section[j++] = line;
section[j] = 0;
strtolower(section);
}
continue;
}
if(equali(section, "settings"))
{
new k[64], v[192];
k[0]=0; v[0]=0;
parse(line, k, cm(k), v, cm(v));
strtolower(k);
StripQuotes(v);
if(equali(k, "access_flags"))
{
copy(g_szAccessFlags, cm(g_szAccessFlags), v);
g_iAccessBits = read_flags(g_szAccessFlags);
}
else if(equali(k, "access_team_tt"))
{
g_iAccessTeamTT = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "access_team_ct"))
{
g_iAccessTeamCT = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "weapon_usage"))
{
g_iWeaponUsage = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "deploy_mode_access"))
{
g_iDeployModeAccess = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "deploy_mode_default"))
{
g_iDeployModeDefault = clamp(str_to_num(v), 0, 2);
}
else if(equali(k, "autodeploy_threshold"))
{
g_flAutoThreshold = floatclamp(str_to_float(v), 0.0, 9999.0);
}
else if(equali(k, "deploys_per_round"))
{
g_iDeploysPerRound = max(0, str_to_num(v));
}
else if(equali(k, "fall_speed_access"))
{
g_iFallSpeedAccess = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "fall_speed_default"))
{
copy(g_szFallSpeedDefault, cm(g_szFallSpeedDefault), v);
}
else if(equali(k, "3rd_person_view_access"))
{
g_iThirdAccess = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "3rd_person_view_default"))
{
g_iThirdDefault = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "model_access"))
{
g_iModelAccess = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "model_default"))
{
copy(g_szModelDefault, cm(g_szModelDefault), v);
}
else if(equali(k, "trail_access"))
{
g_iTrailAccess = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "trail_default"))
{
copy(g_szTrailDefault, cm(g_szTrailDefault), v);
}
else if(equali(k, "trail_color_access"))
{
g_iTrailColorAccess = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "trail_color_default"))
{
copy(g_szTrailColorDefault, cm(g_szTrailColorDefault), v);
}
else if(equali(k, "trail_length_access"))
{
g_iTrailLengthAccess = clamp(str_to_num(v), 0, 1);
}
else if(equali(k, "trail_length_default"))
{
copy(g_szTrailLengthDefault, cm(g_szTrailLengthDefault), v);
}
else if(equali(k, "trail_width"))
{
g_iTrailWidth = clamp(str_to_num(v), 0, 255);
}
else if(equali(k, "deploy_sound"))
{
copy(g_szDeploySound, cm(g_szDeploySound), v);
}
}
else if(equali(section, "speeds"))
{
if(g_iSpeedsNum >= MAX_SPEEDS) continue;
new sSpeed[32], sName[64];
sSpeed[0]=0; sName[0]=0;
parse(line, sSpeed, cm(sSpeed), sName, cm(sName));
StripQuotes(sSpeed);
StripQuotes(sName);
if(!sSpeed[0] || !sName[0]) continue;
copy(g_szSpeedName[g_iSpeedsNum], cm(g_szSpeedName[]), sName);
g_flSpeedValue[g_iSpeedsNum] = str_to_float(sSpeed);
g_iSpeedsNum++;
}
else if(equali(section, "models"))
{
if(g_iModelsNum >= MAX_MODELS) continue;
new path[160], name[64];
path[0]=0; name[0]=0;
parse(line, path, cm(path), name, cm(name));
StripQuotes(path);
StripQuotes(name);
if(!path[0]) continue;
copy(g_szModelPath[g_iModelsNum], cm(g_szModelPath[]), path);
if(name[0])
copy(g_szModelName[g_iModelsNum], cm(g_szModelName[]), name);
else
formatex(g_szModelName[g_iModelsNum], cm(g_szModelName[]), "Модель %d", g_iModelsNum + 1);
if(bPrecacheOnly)
{
if(containi(path, ".mdl") != -1)
precache_model(path);
}
g_iModelsNum++;
}
else if(equali(section, "trails"))
{
if(g_iTrailsNum >= MAX_TRAILS) continue;
new path[160], name[64];
path[0]=0; name[0]=0;
parse(line, path, cm(path), name, cm(name));
StripQuotes(path);
StripQuotes(name);
if(!path[0]) continue;
copy(g_szTrailPath[g_iTrailsNum], cm(g_szTrailPath[]), path);
if(name[0])
copy(g_szTrailName[g_iTrailsNum], cm(g_szTrailName[]), name);
else
formatex(g_szTrailName[g_iTrailsNum], cm(g_szTrailName[]), "Хвост %d", g_iTrailsNum + 1);
if(bPrecacheOnly)
{
if(containi(path, ".spr") != -1)
g_iTrailSpriteIdx[g_iTrailsNum] = precache_model(path);
}
else
{
g_iTrailSpriteIdx[g_iTrailsNum] = engfunc(EngFunc_ModelIndex, path);
}
g_iTrailsNum++;
}
else if(equali(section, "trail_colors"))
{
if(g_iTrailColorsNum >= MAX_TRAIL_COLORS) continue;
new rgb[32], name[64];
rgb[0]=0; name[0]=0;
parse(line, rgb, cm(rgb), name, cm(name));
StripQuotes(rgb);
StripQuotes(name);
if(!rgb[0]) continue;
new sr[8], sg[8], sb[8];
sr[0]=sg[0]=sb[0]=0;
parse(rgb, sr, cm(sr), sg, cm(sg), sb, cm(sb));
new r = clamp(str_to_num(sr), 0, 255);
new g = clamp(str_to_num(sg), 0, 255);
new b = clamp(str_to_num(sb), 0, 255);
g_iTrailColorRGB[g_iTrailColorsNum][0] = r;
g_iTrailColorRGB[g_iTrailColorsNum][1] = g;
g_iTrailColorRGB[g_iTrailColorsNum][2] = b;
if(name[0])
copy(g_szTrailColorName[g_iTrailColorsNum], cm(g_szTrailColorName[]), name);
else
formatex(g_szTrailColorName[g_iTrailColorsNum], cm(g_szTrailColorName[]), "Цвет %d", g_iTrailColorsNum + 1);
g_iTrailColorsNum++;
}
else if(equali(section, "trail_lengths"))
{
if(g_iTrailLengthsNum >= MAX_TRAIL_LENGTHS) continue;
new lenStr[16], name[64];
lenStr[0]=0; name[0]=0;
parse(line, lenStr, cm(lenStr), name, cm(name));
StripQuotes(lenStr);
StripQuotes(name);
if(!lenStr[0]) continue;
g_iTrailLengthVal[g_iTrailLengthsNum] = max(0, str_to_num(lenStr));
if(name[0])
copy(g_szTrailLengthName[g_iTrailLengthsNum], cm(g_szTrailLengthName[]), name);
else
formatex(g_szTrailLengthName[g_iTrailLengthsNum], cm(g_szTrailLengthName[]), "Длина %d", g_iTrailLengthsNum + 1);
g_iTrailLengthsNum++;
}
}
fclose(fp);
ResolveDefaults();
NormalizeDeploySound(bPrecacheOnly);
}
stock ResolveDefaults()
{
g_iDefaultSpeedIndex = 0;
if(g_iSpeedsNum > 0)
{
if(g_szFallSpeedDefault[0])
{
new bool:isIndex = true;
for(new j=0; g_szFallSpeedDefault[j]; j++)
{
if(g_szFallSpeedDefault[j] == '-' || g_szFallSpeedDefault[j] == '.' )
{
isIndex = false;
break;
}
}
if(isIndex)
{
new idx = str_to_num(g_szFallSpeedDefault);
if(idx >= 0 && idx < g_iSpeedsNum)
g_iDefaultSpeedIndex = idx;
}
else
{
new Float:f = str_to_float(g_szFallSpeedDefault);
for(new i=0;i<g_iSpeedsNum;i++)
{
if(floatabs(g_flSpeedValue - f) < 0.01)
{
g_iDefaultSpeedIndex = i;
break;
}
}
}
}
else
{
for(new i=0;i<g_iSpeedsNum;i++)
{
if(floatabs(g_flSpeedValue - (-100.0)) < 0.01)
{
g_iDefaultSpeedIndex = i;
break;
}
}
}
}
g_iDefaultModelIndex = -1;
if(g_iModelsNum > 0)
{
if(g_szModelDefault[0])
{
for(new i=0;i<g_iModelsNum;i++)
{
if(equali(g_szModelPath, g_szModelDefault))
{
g_iDefaultModelIndex = i;
break;
}
}
}
}
g_iDefaultTrailIndex = -1;
if(g_iTrailsNum > 0)
{
if(g_szTrailDefault[0])
{
for(new i=0;i<g_iTrailsNum;i++)
{
if(equali(g_szTrailPath, g_szTrailDefault))
{
g_iDefaultTrailIndex = i;
break;
}
}
}
}
g_iDefaultTrailColorIndex = -1;
if(g_iTrailColorsNum > 0)
{
if(g_szTrailColorDefault[0])
{
new sr[8], sg[8], sb[8];
sr[0]=sg[0]=sb[0]=0;
parse(g_szTrailColorDefault, sr, cm(sr), sg, cm(sg), sb, cm(sb));
new dr = clamp(str_to_num(sr), 0, 255);
new dg = clamp(str_to_num(sg), 0, 255);
new db = clamp(str_to_num(sb), 0, 255);
for(new i=0;i<g_iTrailColorsNum;i++)
{
if(g_iTrailColorRGB[0] == dr && g_iTrailColorRGB[1] == dg && g_iTrailColorRGB[2] == db)
{
g_iDefaultTrailColorIndex = i;
break;
}
}
}
}
g_iDefaultTrailLengthIndex = 0;
if(g_iTrailLengthsNum > 0)
{
if(g_szTrailLengthDefault[0])
{
new dl = max(0, str_to_num(g_szTrailLengthDefault));
for(new i=0;i<g_iTrailLengthsNum;i++)
{
if(g_iTrailLengthVal == dl)
{
g_iDefaultTrailLengthIndex = i;
break;
}
}
}
if(g_iDefaultTrailLengthIndex >= g_iTrailLengthsNum)
g_iDefaultTrailLengthIndex = 0;
}
}
stock NormalizeDeploySound(bool:bPrecacheOnly)
{
if(!g_szDeploySound[0])
return;
if(containi(g_szDeploySound, "sound/") == 0)
{
new tmp[128];
copy(tmp, cm(tmp), g_szDeploySound[6]);
copy(g_szDeploySound, cm(g_szDeploySound), tmp);
}
else if(containi(g_szDeploySound, "sound\\") == 0)
{
new tmp2[128];
copy(tmp2, cm(tmp2), g_szDeploySound[6]);
copy(g_szDeploySound, cm(g_szDeploySound), tmp2);
}
if(bPrecacheOnly)
precache_sound(g_szDeploySound);
}
stock ResetPlayerState(id)
{
g_iParaEnt[id] = 0;
g_bParaDeployed[id] = false;
g_iDeployMode[id] = g_iDeployModeDefault;
g_iSpeedIndex[id] = g_iDefaultSpeedIndex;
g_bThird[id] = (g_iThirdDefault != 0);
if(g_iModelsNum > 0)
{
g_iModelIndex[id] = g_iDefaultModelIndex;
if(g_iModelIndex[id] >= g_iModelsNum) g_iModelIndex[id] = -1;
}
else
{
g_iModelIndex[id] = 0;
}
g_iLastWeapon[id] = 0;
}
stock HasParachuteAccess(id)
{
if(g_szAccessFlags[0] && (get_user_flags(id) & g_iAccessBits) == 0)
return false;
new CsTeams:t = cs_get_user_team(id);
if(t == CS_TEAM_T && !g_iAccessTeamTT) return false;
if(t == CS_TEAM_CT && !g_iAccessTeamCT) return false;
return true;
}
stock GetVaultKey(id, key[], keylen)
{
new auth[32];
get_user_authid(id, auth, cm(auth));
if(equali(auth, "BOT") || equali(auth, "HLTV") || equali(auth, "STEAM_ID_LAN") || equali(auth, "VALVE_ID_LAN") || equali(auth, "STEAM_ID_PENDING"))
{
new ip[32];
get_user_ip(id, ip, cm(ip), 1);
formatex(key, keylen, "IP_%s", ip);
return;
}
formatex(key, keylen, "SID_%s", auth);
}
stock LoadPlayerData(id)
{
ResetPlayerState(id);
if(g_iVault == INVALID_HANDLE)
return;
new key[64];
GetVaultKey(id, key, cm(key));
new data[128];
if(!nvault_get(g_iVault, key, data, cm(data)))
return;
new s1[8], s2[8], s3[8], s4[8], s5[8], s6[8], s7[8];
s1[0]=s2[0]=s3[0]=s4[0]=s5[0]=s6[0]=s7[0]=0;
parse(data, s1, cm(s1), s2, cm(s2), s3, cm(s3), s4, cm(s4), s5, cm(s5), s6, cm(s6), s7, cm(s7));
new m = str_to_num(s1);
new sp = str_to_num(s2);
new th = str_to_num(s3);
new md = str_to_num(s4);
new tr = str_to_num(s5);
new tc = str_to_num(s6);
new tl = str_to_num(s7);
g_iDeployMode[id] = clamp(m, 0, 2);
if(g_iSpeedsNum > 0)
g_iSpeedIndex[id] = clamp(sp, 0, g_iSpeedsNum-1);
else
g_iSpeedIndex[id] = 0;
g_bThird[id] = (th != 0);
if(g_iModelsNum > 0)
{
if(md < 0)
g_iModelIndex[id] = -1;
else
g_iModelIndex[id] = clamp(md, 0, g_iModelsNum-1);
}
else
{
g_iModelIndex[id] = 0;
}
if(g_iTrailsNum > 0)
{
if(tr < 0)
g_iTrailIndex[id] = -1;
else
g_iTrailIndex[id] = clamp(tr, 0, g_iTrailsNum-1);
}
else
{
g_iTrailIndex[id] = 0;
}
if(g_iTrailColorsNum > 0)
{
if(tc < 0)
g_iTrailColorIndex[id] = -1;
else
g_iTrailColorIndex[id] = clamp(tc, 0, g_iTrailColorsNum-1);
}
else
{
g_iTrailColorIndex[id] = 0;
}
if(g_iTrailLengthsNum > 0)
{
if(tl < 0)
g_iTrailLengthIndex[id] = -1;
else
g_iTrailLengthIndex[id] = clamp(tl, 0, g_iTrailLengthsNum-1);
}
else
{
g_iTrailLengthIndex[id] = 0;
}
}
stock SavePlayerData(id)
{
if(!is_user_connected(id)) return;
if(!HasParachuteAccess(id)) return;
if(g_iVault == INVALID_HANDLE) return;
new key[64];
GetVaultKey(id, key, cm(key));
new data[128];
formatex(data, cm(data), "%d %d %d %d %d %d %d",
g_iDeployMode[id],
g_iSpeedIndex[id],
g_bThird[id] ? 1 : 0,
g_iModelIndex[id],
g_iTrailIndex[id],
g_iTrailColorIndex[id],
g_iTrailLengthIndex[id]
);
nvault_set(g_iVault, key, data);
}
stock StripQuotes(str[])
{
if(!str[0]) return;
new len = strlen(str);
if(len >= 2 && str[0] == '"' && str[len-1] == '"')
{
str[len-1] = 0;
for(new i=0; i<len; i++)
str = str[i+1];
}
}