Romania XPlayZM.CSBlackDevil.COM # Zombie Plague UltimateX [ Respawn + FDL + VIP 22-12PM + POINTS] ® 17/32
Unknown ❤ RESPAWN.ZAFEST.RO | VIP FREE | FREE FOR ALL ❤ 0/32
Germany ELITE.WORLDCS.RO # [SKINS |VIP + REVIVE| RANKS] 22/32
Promouvoir serveur
csb-longjump-trainer / addons / amxmodx / scripting / csb_longjump_trainer.sma
208 lines 5.1 KiB Pawn Raw Permalink History
/*
 * CSB Longjump Trainer
 * Copyright (C) 2026 counter-strike-boost.com
 *
 * A KZ training tool. In PlayerPreThink it samples velocity and origin between
 * takeoff and landing to measure each long jump: distance covered, pre-strafe
 * speed at takeoff, number of jump inputs (bhop taps) and a strafe-sync
 * percentage (fraction of airborne frames where the player was gaining speed).
 * The result is shown in a HUD panel and the personal best per map is stored in
 * an nVault keyed by SteamID.
 *
 * Inspired by LJ Stats / KZ trainer plugins. Independent GPL re-implementation.
 *
 * 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>
#include <nvault>

new const PLUGIN[] = "CSB Longjump Trainer"
new const VERSION[] = "1.0.0"
new const AUTHOR[]  = "counter-strike-boost.com"

#if !defined FL_ONGROUND
    #define FL_ONGROUND (1<<9)
#endif
#if !defined IN_JUMP
    #define IN_JUMP 2
#endif

new g_pEnabled, g_pMinDist

new bool:g_bInAir[33]
new Float:g_fTakeoff[33][3]
new Float:g_fPreSpeed[33]
new Float:g_fLastSpeed[33]
new g_iJumps[33]
new g_iSyncGain[33]
new g_iSyncTotal[33]
new bool:g_bLastJump[33]

new g_vault
new g_szMap[32]

public plugin_init()
{
    register_plugin(PLUGIN, VERSION, AUTHOR)

    g_pEnabled = register_cvar("csb_lj_enabled", "1")
    g_pMinDist = register_cvar("csb_lj_mindist", "200")

    register_forward(FM_PlayerPreThink, "fwPreThink")

    register_clcmd("say /ljbest", "cmdBest")
    register_clcmd("say_team /ljbest", "cmdBest")

    get_mapname(g_szMap, charsmax(g_szMap))
    g_vault = nvault_open("csb_ljtrainer")
}

public plugin_end()
{
    nvault_close(g_vault)
}

public client_putinserver(id)
{
    resetJump(id)
}

resetJump(id)
{
    g_bInAir[id]     = false
    g_iJumps[id]     = 0
    g_iSyncGain[id]  = 0
    g_iSyncTotal[id] = 0
    g_fPreSpeed[id]  = 0.0
    g_fLastSpeed[id] = 0.0
    g_bLastJump[id]  = false
}

public fwPreThink(id)
{
    if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id))
        return FMRES_IGNORED

    new flags = pev(id, pev_flags)
    new bool:onground = (flags & FL_ONGROUND) != 0
    new btn = pev(id, pev_button)

    static Float:fVel[3]
    pev(id, pev_velocity, fVel)
    new Float:hspeed = floatsqroot(fVel[0] * fVel[0] + fVel[1] * fVel[1])

    if (onground)
    {
        /* remember the last grounded speed as the pre-strafe speed */
        g_fPreSpeed[id] = hspeed

        if (g_bInAir[id])
        {
            g_bInAir[id] = false
            landed(id)
        }
    }
    else
    {
        if (!g_bInAir[id])
        {
            g_bInAir[id] = true
            pev(id, pev_origin, g_fTakeoff[id])
            g_iJumps[id] = 1
            g_iSyncGain[id] = 0
            g_iSyncTotal[id] = 0
            g_fLastSpeed[id] = hspeed
        }
        else
        {
            if ((btn & IN_JUMP) && !g_bLastJump[id])
                g_iJumps[id]++

            g_iSyncTotal[id]++
            if (hspeed > g_fLastSpeed[id])
                g_iSyncGain[id]++

            g_fLastSpeed[id] = hspeed
        }
    }

    g_bLastJump[id] = (btn & IN_JUMP) != 0
    return FMRES_IGNORED
}

landed(id)
{
    static Float:fNow[3]
    pev(id, pev_origin, fNow)

    new Float:dx = fNow[0] - g_fTakeoff[id][0]
    new Float:dy = fNow[1] - g_fTakeoff[id][1]
    new Float:dist = floatsqroot(dx * dx + dy * dy)

    if (dist < get_pcvar_float(g_pMinDist))
        return

    new sync = 0
    if (g_iSyncTotal[id] > 0)
        sync = (g_iSyncGain[id] * 100) / g_iSyncTotal[id]

    new Float:best = getBest(id)
    new bool:isPB = false

    if (dist > best)
    {
        isPB = true
        setBest(id, dist)
    }

    set_hudmessage(0, 255, 100, -1.0, 0.62, 0, 0.0, 4.0, 0.1, 0.3, -1)
    show_hudmessage(id, "Distance: %.1f u^nPrestrafe: %.0f u/s^nJumps: %d    Sync: %d%%^n%s",
        dist, g_fPreSpeed[id], g_iJumps[id], sync, isPB ? "*** PERSONAL BEST ***" : " ")
}

Float:getBest(id)
{
    new auth[35]
    get_user_authid(id, auth, charsmax(auth))

    new key[80]
    formatex(key, charsmax(key), "%s_%s", g_szMap, auth)

    new data[16]
    if (nvault_get(g_vault, key, data, charsmax(data)))
        return str_to_float(data)

    return 0.0
}

setBest(id, Float:dist)
{
    new auth[35]
    get_user_authid(id, auth, charsmax(auth))

    new key[80]
    formatex(key, charsmax(key), "%s_%s", g_szMap, auth)

    new data[16]
    formatex(data, charsmax(data), "%f", dist)
    nvault_set(g_vault, key, data)
}

public cmdBest(id)
{
    new Float:best = getBest(id)

    if (best <= 0.0)
        client_print(id, print_chat, "^x04[CSB]^x01 No recorded long jump yet on %s.", g_szMap)
    else
        client_print(id, print_chat, "^x04[CSB]^x01 Your best long jump on %s: %.1f units.", g_szMap, best)

    return PLUGIN_HANDLED
}