Promover servidor
csb-admin-grab / addons / amxmodx / scripting / csb_admin_grab.sma
190 lines 4.7 KiB Pawn Raw Permalink History
/*
 * 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 <https://www.gnu.org/licenses/> for details.
 */

#include <amxmodx>
#include <fakemeta>

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
}