/*
* CSB Semiclip
* Copyright (C) 2026 counter-strike-boost.com
*
* Lets teammates pass through each other instead of blocking. During each
* player's physics window (between his PreThink and PostThink) every close
* teammate is temporarily made non-solid, so movement never collides with a
* friend; the solidity is restored immediately after. AddToFullPack mirrors
* the non-solid state into that client's own prediction so there is no
* client-side jitter. Shooting is unaffected because the change only lasts for
* the owner's own physics step. Essential on Deathrun, KZ, HNS and Jailbreak.
*
* Inspired by the classic "Semiclip" by ConnorMcLeod. 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 Semiclip"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define SOLID_NOT 0
#define SOLID_SLIDEBOX 3
new g_pEnabled, g_pTeamOnly, g_pRange
new g_iMaxPlayers
new g_iSaved[33] /* saved pev_solid while non-solid, -1 = untouched */
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_semiclip", "1")
g_pTeamOnly = register_cvar("csb_semiclip_teamonly", "1")
g_pRange = register_cvar("csb_semiclip_range", "160")
register_forward(FM_PlayerPreThink, "fw_PreThink")
register_forward(FM_PlayerPostThink, "fw_PostThink")
register_forward(FM_AddToFullPack, "fw_AddToFullPack", 1)
g_iMaxPlayers = get_maxplayers()
for (new i = 1; i <= 32; i++)
g_iSaved[i] = -1
}
/* Make close teammates non-solid for the duration of id's physics step. */
public fw_PreThink(id)
{
if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id))
return FMRES_IGNORED
new Float:range = get_pcvar_float(g_pRange)
new bool:teamOnly = get_pcvar_num(g_pTeamOnly) != 0
new team = get_user_team(id)
new Float:oPos[3], Float:tPos[3]
pev(id, pev_origin, oPos)
for (new i = 1; i <= g_iMaxPlayers; i++)
{
if (i == id || !is_user_alive(i))
continue
if (teamOnly && get_user_team(i) != team)
continue
pev(i, pev_origin, tPos)
if (range > 0.0 && get_distance_f(oPos, tPos) > range)
continue
if (g_iSaved[i] != -1) /* already made non-solid this step */
continue
g_iSaved[i] = pev(i, pev_solid)
set_pev(i, pev_solid, SOLID_NOT)
}
return FMRES_IGNORED
}
/* Restore everything we touched once id's movement has been resolved. */
public fw_PostThink(id)
{
for (new i = 1; i <= g_iMaxPlayers; i++)
{
if (g_iSaved[i] == -1)
continue
if (pev_valid(i))
set_pev(i, pev_solid, g_iSaved[i])
g_iSaved[i] = -1
}
return FMRES_IGNORED
}
/* Kill client-side collision prediction between teammates. */
public fw_AddToFullPack(es, e, ent, host, hostflags, player, pSet)
{
if (!get_pcvar_num(g_pEnabled))
return FMRES_IGNORED
if (ent < 1 || ent > g_iMaxPlayers || host < 1 || host > g_iMaxPlayers || ent == host)
return FMRES_IGNORED
if (!is_user_alive(ent) || !is_user_alive(host))
return FMRES_IGNORED
if (get_pcvar_num(g_pTeamOnly) && get_user_team(ent) != get_user_team(host))
return FMRES_IGNORED
set_es(es, ES_Solid, SOLID_NOT)
return FMRES_HANDLED
}
public client_disconnected(id)
{
g_iSaved[id] = -1
}