/*
* CSB Admin Grab
* Copyright (C) 2026 counter-strike-boost.com
*
* Lets an admin grab a player (or a physics entity) with +grab: while the key is
* held the target is pinned in the air at a fixed distance along the admin's aim
* direction, updated every frame in PlayerPreThink. invnext / invprev change the
* hold distance, and releasing +grab throws the target with velocity along the
* aim vector.
*
* Inspired by Admin Grab by KRoTaL. 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 Admin Grab"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define ACCESS_FLAG ADMIN_BAN
new g_pEnabled, g_pThrow, g_pMaxDist
new g_iTarget[33]
new Float:g_fDist[33]
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_grab_enabled", "1")
g_pThrow = register_cvar("csb_grab_throw", "900")
g_pMaxDist = register_cvar("csb_grab_maxdist", "800")
register_clcmd("+grab", "cmdGrabStart")
register_clcmd("-grab", "cmdGrabStop")
register_clcmd("invnext", "cmdNext")
register_clcmd("invprev", "cmdPrev")
register_forward(FM_PlayerPreThink, "fwPreThink")
}
public client_putinserver(id)
{
g_iTarget[id] = 0
g_fDist[id] = 120.0
}
public client_disconnected(id)
{
g_iTarget[id] = 0
}
bool:hasAccess(id)
{
return (get_user_flags(id) & ACCESS_FLAG) != 0
}
public cmdGrabStart(id)
{
if (!get_pcvar_num(g_pEnabled) || !hasAccess(id) || !is_user_alive(id))
return PLUGIN_HANDLED
new ent, body
get_user_aiming(id, ent, body, 4096)
if (ent < 1 || !pev_valid(ent))
{
client_print(id, print_chat, "[CSB] Nothing grabbable in your crosshair.")
return PLUGIN_HANDLED
}
/* set the hold distance to the current distance to the target */
static Float:fMe[3], Float:fThem[3]
pev(id, pev_origin, fMe)
pev(ent, pev_origin, fThem)
g_fDist[id] = vector_distance(fMe, fThem)
g_iTarget[id] = ent
new cap = get_pcvar_num(g_pMaxDist)
if (g_fDist[id] > float(cap))
g_fDist[id] = float(cap)
client_print(id, print_chat, "[CSB] Grabbed entity %d. Use mouse wheel to push/pull, release to throw.", ent)
return PLUGIN_HANDLED
}
public cmdGrabStop(id)
{
new ent = g_iTarget[id]
g_iTarget[id] = 0
if (ent < 1 || !pev_valid(ent))
return PLUGIN_HANDLED
/* throw along the admin aim vector */
static Float:fAng[3], Float:fFwd[3]
pev(id, pev_v_angle, fAng)
angle_vector(fAng, ANGLEVECTOR_FORWARD, fFwd)
new Float:speed = get_pcvar_float(g_pThrow)
static Float:fVel[3]
fVel[0] = fFwd[0] * speed
fVel[1] = fFwd[1] * speed
fVel[2] = fFwd[2] * speed + 100.0
set_pev(ent, pev_velocity, fVel)
return PLUGIN_HANDLED
}
public cmdNext(id)
{
if (g_iTarget[id] > 0)
{
g_fDist[id] += 30.0
new cap = get_pcvar_num(g_pMaxDist)
if (g_fDist[id] > float(cap))
g_fDist[id] = float(cap)
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE
}
public cmdPrev(id)
{
if (g_iTarget[id] > 0)
{
g_fDist[id] -= 30.0
if (g_fDist[id] < 40.0)
g_fDist[id] = 40.0
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE
}
public fwPreThink(id)
{
new ent = g_iTarget[id]
if (ent < 1)
return FMRES_IGNORED
if (!is_user_alive(id) || !pev_valid(ent))
{
g_iTarget[id] = 0
return FMRES_IGNORED
}
static Float:fEye[3], Float:fOfs[3], Float:fAng[3], Float:fFwd[3]
pev(id, pev_origin, fEye)
pev(id, pev_view_ofs, fOfs)
fEye[0] += fOfs[0]
fEye[1] += fOfs[1]
fEye[2] += fOfs[2]
pev(id, pev_v_angle, fAng)
angle_vector(fAng, ANGLEVECTOR_FORWARD, fFwd)
static Float:fDest[3]
fDest[0] = fEye[0] + fFwd[0] * g_fDist[id]
fDest[1] = fEye[1] + fFwd[1] * g_fDist[id]
fDest[2] = fEye[2] + fFwd[2] * g_fDist[id]
/* hold it in place: zero velocity and set origin each frame */
static Float:fZero[3]
fZero[0] = 0.0
fZero[1] = 0.0
fZero[2] = 0.0
set_pev(ent, pev_velocity, fZero)
engfunc(EngFunc_SetOrigin, ent, fDest)
return FMRES_IGNORED
}