/* * CSB Parachute * Copyright (C) 2026 counter-strike-boost.com * * Hold +use while falling to deploy a parachute. In PlayerPreThink the plugin * clamps the downward velocity to a configurable speed and shows a parachute * model attached above the player (pev_aiment / MOVETYPE_FOLLOW); it is removed * the moment the player lands or lets go of +use. Cvars control the fall speed, * a VIP-only flag and a per-round use limit. * * Inspired by the well-known "Parachute" plugin by KRoTaL and later VEN. This is * an independent GPL re-implementation; no original code is reused. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. It is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See for details. */ #include #include new const PLUGIN[] = "CSB Parachute" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" #define PARA_MODEL "models/csb_parachute.mdl" new g_iChute[33] new g_iUsed[33] new bool:g_bModelOk new g_pEnabled, g_pFallSpeed, g_pVipFlag, g_pMaxPerRound public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) g_pEnabled = register_cvar("csb_para_enabled", "1") g_pFallSpeed = register_cvar("csb_para_fallspeed", "100") g_pVipFlag = register_cvar("csb_para_vipflag", "") g_pMaxPerRound = register_cvar("csb_para_maxperround", "0") /* 0 = unlimited */ register_forward(FM_PlayerPreThink, "fwPreThink") register_event("ResetHUD", "evSpawn", "b") register_event("HLTV", "evNewRound", "a", "1=0", "2=0") } public plugin_precache() { if (file_exists(PARA_MODEL)) { precache_model(PARA_MODEL) g_bModelOk = true } else { log_amx("[CSB Parachute] %s not found - the parachute will slow the fall but show no model.", PARA_MODEL) } } public client_putinserver(id) { g_iChute[id] = 0 g_iUsed[id] = 0 } public client_disconnected(id) { removeChute(id) g_iChute[id] = 0 } public evSpawn(id) { removeChute(id) g_iUsed[id] = 0 } public evNewRound() { for (new i = 1; i <= 32; i++) g_iUsed[i] = 0 } vipAllowed(id) { new flagStr[8] get_pcvar_string(g_pVipFlag, flagStr, charsmax(flagStr)) if (!flagStr[0]) return 1 new need = read_flags(flagStr) return (get_user_flags(id) & need) ? 1 : 0 } public fwPreThink(id) { if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id)) { if (g_iChute[id]) removeChute(id) return } new flags = pev(id, pev_flags) if (flags & FL_ONGROUND) { if (g_iChute[id]) removeChute(id) return } new Float:vel[3] pev(id, pev_velocity, vel) new button = pev(id, pev_button) /* deploy only while +use is held and the player is actually descending */ if ((button & IN_USE) && vel[2] < 0.0 && vipAllowed(id)) { new maxUse = get_pcvar_num(g_pMaxPerRound) if (!g_iChute[id]) { if (maxUse > 0 && g_iUsed[id] >= maxUse) return deployChute(id) g_iUsed[id]++ } new Float:fall = get_pcvar_float(g_pFallSpeed) if (fall < 10.0) fall = 10.0 vel[2] = -fall set_pev(id, pev_velocity, vel) } else if (g_iChute[id]) { removeChute(id) } } deployChute(id) { if (!g_bModelOk) { g_iChute[id] = -1 /* marker: active but no visible model */ return } new ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target")) if (!pev_valid(ent)) { g_iChute[id] = -1 return } engfunc(EngFunc_SetModel, ent, PARA_MODEL) set_pev(ent, pev_movetype, MOVETYPE_FOLLOW) set_pev(ent, pev_aiment, id) set_pev(ent, pev_owner, id) set_pev(ent, pev_sequence, 0) set_pev(ent, pev_frame, 0.0) set_pev(ent, pev_framerate, 1.0) set_pev(ent, pev_animtime, get_gametime()) new Float:origin[3] pev(id, pev_origin, origin) set_pev(ent, pev_origin, origin) g_iChute[id] = ent } removeChute(id) { new ent = g_iChute[id] if (ent > 0 && pev_valid(ent)) engfunc(EngFunc_RemoveEntity, ent) g_iChute[id] = 0 }