/*
* CSB Surf Helper
* Copyright (C) 2026 counter-strike-boost.com
*
* Quality-of-life for surf servers. Fall damage is cancelled, buying is blocked,
* a sensible sv_airaccelerate is set, a live speed HUD shows horizontal velocity,
* /stuck teleports a wedged player to a spawn point, and death triggers an
* instant respawn so a surf map never leaves anyone waiting.
*
* 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
#include
new const PLUGIN[] = "CSB Surf Helper"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled, g_pNoFall, g_pNoBuy, g_pAirAccel, g_pRespawn, g_pStuckCd
new Float:g_fLastStuck[33]
new g_iHudSync
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_surf_enabled", "1")
g_pNoFall = register_cvar("csb_surf_nofall", "1")
g_pNoBuy = register_cvar("csb_surf_nobuy", "1")
g_pAirAccel = register_cvar("csb_surf_airaccel", "100")
g_pRespawn = register_cvar("csb_surf_respawn", "1")
g_pStuckCd = register_cvar("csb_surf_stuck_cooldown", "5.0")
RegisterHam(Ham_TakeDamage, "player", "fwTakeDamage", 0)
register_event("DeathMsg", "eventDeath", "a", "1>0")
register_clcmd("say /stuck", "cmdStuck")
register_clcmd("say_team /stuck", "cmdStuck")
register_clcmd("buy", "blockBuy")
register_clcmd("buyammo", "blockBuy")
g_iHudSync = CreateHudSyncObj()
set_task(0.3, "taskSpeed", 0, _, _, "b")
}
public plugin_cfg()
{
if (get_pcvar_num(g_pEnabled))
{
new pAir = get_cvar_pointer("sv_airaccelerate")
if (pAir)
set_pcvar_num(pAir, get_pcvar_num(g_pAirAccel))
}
}
public client_putinserver(id)
{
g_fLastStuck[id] = 0.0
}
public fwTakeDamage(victim, inflictor, attacker, Float:damage, damagebits)
{
if (!get_pcvar_num(g_pEnabled) || !get_pcvar_num(g_pNoFall))
return HAM_IGNORED
if (damagebits & DMG_FALL)
{
SetHamParamFloat(4, 0.0)
return HAM_SUPERCEDE
}
return HAM_IGNORED
}
public cmdStuck(id)
{
if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id))
return PLUGIN_HANDLED
if (get_gametime() - g_fLastStuck[id] < get_pcvar_float(g_pStuckCd))
{
client_print(id, print_chat, "[CSB] /stuck is on cooldown.")
return PLUGIN_HANDLED
}
new spawn = engfunc(EngFunc_FindEntityByString, 0, "classname", "info_player_start")
if (spawn <= 0)
spawn = engfunc(EngFunc_FindEntityByString, 0, "classname", "info_player_deathmatch")
if (spawn <= 0)
{
client_print(id, print_chat, "[CSB] No spawn point found.")
return PLUGIN_HANDLED
}
static Float:fPos[3]
pev(spawn, pev_origin, fPos)
engfunc(EngFunc_SetOrigin, id, fPos)
static Float:fZero[3]
set_pev(id, pev_velocity, fZero)
g_fLastStuck[id] = get_gametime()
client_print(id, print_chat, "[CSB] Teleported to a spawn point.")
return PLUGIN_HANDLED
}
public blockBuy(id)
{
if (get_pcvar_num(g_pEnabled) && get_pcvar_num(g_pNoBuy))
return PLUGIN_HANDLED
return PLUGIN_CONTINUE
}
public eventDeath()
{
new victim = read_data(2)
if (!get_pcvar_num(g_pEnabled) || !get_pcvar_num(g_pRespawn))
return
if (victim >= 1 && victim <= 32)
set_task(2.0, "taskRespawn", victim)
}
public taskRespawn(id)
{
if (!is_user_connected(id) || is_user_alive(id))
return
if (get_user_team(id) == 1 || get_user_team(id) == 2)
ExecuteHamB(Ham_CS_RoundRespawn, id)
}
public taskSpeed()
{
if (!get_pcvar_num(g_pEnabled))
return
new players[32], num
get_players(players, num, "a")
for (new i = 0; i < num; i++)
{
new id = players[i]
static Float:fVel[3]
pev(id, pev_velocity, fVel)
new speed = floatround(floatsqroot(fVel[0]*fVel[0] + fVel[1]*fVel[1]))
set_hudmessage(0, 255, 200, 0.45, 0.80, 0, 0.0, 0.4, 0.0, 0.0, -1)
ShowSyncHudMsg(id, g_iHudSync, "%d u/s", speed)
}
}