Загрузка данных



#include <amxmodx>
#include <engine>
#include <fakemeta>
#include <fun>

#define PLUGIN  "VC Vehicles"
#define VERSION "0.2"
#define AUTHOR  "OpenAI"

#define VEH_CLASS "vc_vehicle"
#define MAX_EDICTS 2049
#define DT 0.02

new g_driver[MAX_EDICTS];
new g_vehicle[33];

new g_old_movetype[33];
new g_old_solid[33];

new g_model[128] = "models/vc_car.mdl";

new g_pc_accel;
new g_pc_brake;
new g_pc_maxspeed;
new g_pc_reverse;
new g_pc_steer;
new g_pc_grip;
new g_pc_drift_grip;
new g_pc_drag;

public plugin_precache()
{
    // A valid model must exist before the map starts.
    // Use a standard CS model as a temporary visual fallback.
    if (file_exists(g_model))
    {
        precache_model(g_model);
    }
    else
    {
        server_print("[VC] models/vc_car.mdl not found; using w_usp.mdl");
        copy(g_model, charsmax(g_model), "models/w_usp.mdl");
        precache_model(g_model);
    }
}

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

    register_clcmd("say /car", "cmd_car");
    register_clcmd("say_team /car", "cmd_car");
    register_clcmd("say /exit", "cmd_exit");

    register_forward(FM_PlayerPreThink, "fw_player_prethink");
    register_forward(FM_Touch, "fw_touch");
    register_think(VEH_CLASS, "vehicle_think");

    g_pc_accel       = register_cvar("vc_accel", "420.0");
    g_pc_brake       = register_cvar("vc_brake", "650.0");
    g_pc_maxspeed    = register_cvar("vc_maxspeed", "750.0");
    g_pc_reverse     = register_cvar("vc_reverse", "220.0");
    g_pc_steer       = register_cvar("vc_steer", "105.0");
    g_pc_grip        = register_cvar("vc_grip", "5.0");
    g_pc_drift_grip  = register_cvar("vc_drift_grip", "1.15");
    g_pc_drag        = register_cvar("vc_drag", "0.992");
}

public client_disconnected(id)
{
    if (g_vehicle[id])
        exit_vehicle(id, true);
}

public cmd_car(id)
{
    if (!is_user_alive(id))
        return PLUGIN_HANDLED;

    if (g_vehicle[id])
    {
        client_print(id, print_chat, "[VC] You are already driving.");
        return PLUGIN_HANDLED;
    }

    new ent = create_entity("info_target");

    if (!ent || ent >= MAX_EDICTS)
    {
        if (ent > 0)
            remove_entity(ent);

        client_print(id, print_chat, "[VC] Could not create vehicle.");
        return PLUGIN_HANDLED;
    }

    new Float:origin[3], Float:angles[3], Float:forward[3];

    pev(id, pev_origin, origin);
    pev(id, pev_v_angle, angles);

    angles[0] = 0.0;
    angles[2] = 0.0;

    engfunc(EngFunc_MakeVectors, angles);
    global_get(glb_v_forward, forward);

    origin[0] += forward[0] * 150.0;
    origin[1] += forward[1] * 150.0;
    origin[2] += 8.0;

    entity_set_string(ent, EV_SZ_classname, VEH_CLASS);
    entity_set_model(ent, g_model);

    entity_set_origin(ent, origin);
    entity_set_size(ent,
        Float:{-42.0, -22.0, -8.0},
        Float:{ 42.0,  22.0, 30.0});

    set_pev(ent, pev_movetype, MOVETYPE_FLY);
    set_pev(ent, pev_solid, SOLID_BBOX);
    set_pev(ent, pev_angles, angles);
    set_pev(ent, pev_velocity, Float:{0.0, 0.0, 0.0});

    // fuser1 = signed forward speed
    // fuser2 = vehicle yaw
    set_pev(ent, pev_fuser1, 0.0);
    set_pev(ent, pev_fuser2, angles[1]);

    g_driver[ent] = 0;

    set_pev(ent, pev_nextthink, get_gametime() + DT);

    client_print(id, print_chat,
        "[VC] Vehicle spawned. Approach it and press E.");

    return PLUGIN_HANDLED;
}

public cmd_exit(id)
{
    if (g_vehicle[id])
        exit_vehicle(id, true);

    return PLUGIN_HANDLED;
}

public fw_player_prethink(id)
{
    if (!is_user_alive(id))
        return FMRES_IGNORED;

    new buttons = pev(id, pev_button);
    new oldbuttons = pev(id, pev_oldbuttons);

    if (g_vehicle[id] > 0)
    {
        if ((buttons & IN_USE) && !(oldbuttons & IN_USE))
        {
            exit_vehicle(id, true);
            return FMRES_IGNORED;
        }

        // Vehicle physics controls movement while driving.
        set_pev(id, pev_velocity, Float:{0.0, 0.0, 0.0});
        return FMRES_IGNORED;
    }

    if ((buttons & IN_USE) && !(oldbuttons & IN_USE))
    {
        new ent = find_ent_by_class(-1, VEH_CLASS);
        new Float:player_origin[3], Float:car_origin[3];

        pev(id, pev_origin, player_origin);

        while (ent > 0)
        {
            if (ent < MAX_EDICTS && pev_valid(ent)
                && g_driver[ent] == 0)
            {
                pev(ent, pev_origin, car_origin);

                if (get_distance_f(player_origin, car_origin) < 110.0)
                {
                    enter_vehicle(id, ent);
                    break;
                }
            }

            ent = find_ent_by_class(ent, VEH_CLASS);
        }
    }

    return FMRES_IGNORED;
}

stock enter_vehicle(id, ent)
{
    g_vehicle[id] = ent;
    g_driver[ent] = id;

    g_old_movetype[id] = pev(id, pev_movetype);
    g_old_solid[id] = pev(id, pev_solid);

    set_pev(id, pev_movetype, MOVETYPE_NOCLIP);
    set_pev(id, pev_solid, SOLID_NOT);

    set_view(id, CAMERA_3RDPERSON);

    client_print(id, print_center,
        "W/S Drive | A/D Steer | SPACE Drift | E Exit");
}

stock exit_vehicle(id, bool:restore_position)
{
    new ent = g_vehicle[id];

    if (!ent)
        return;

    if (pev_valid(ent))
    {
        new Float:origin[3], Float:angles[3];
        new Float:right[3];

        pev(ent, pev_origin, origin);
        pev(ent, pev_angles, angles);

        engfunc(EngFunc_MakeVectors, angles);
        global_get(glb_v_right, right);

        origin[0] += right[0] * 68.0;
        origin[1] += right[1] * 68.0;
        origin[2] += 30.0;

        if (restore_position)
            engfunc(EngFunc_SetOrigin, id, origin);

        g_driver[ent] = 0;
    }

    g_vehicle[id] = 0;

    set_view(id, CAMERA_NONE);
    set_pev(id, pev_movetype, g_old_movetype[id]);
    set_pev(id, pev_solid, g_old_solid[id]);
    set_pev(id, pev_velocity, Float:{0.0, 0.0, 0.0});
}

public vehicle_think(ent)
{
    if (!pev_valid(ent))
        return;

    new Float:speed, Float:yaw;
    pev(ent, pev_fuser1, speed);
    pev(ent, pev_fuser2, yaw);

    new driver = g_driver[ent];

    if (driver > 0 && is_user_alive(driver))
    {
        new buttons = pev(driver, pev_button);

        new Float:accel = get_pcvar_float(g_pc_accel);
        new Float:brake = get_pcvar_float(g_pc_brake);
        new Float:maxspeed = get_pcvar_float(g_pc_maxspeed);
        new Float:reverse = get_pcvar_float(g_pc_reverse);

        new Float:steer_rate = get_pcvar_float(g_pc_steer);
        new Float:grip = get_pcvar_float(g_pc_grip);
        new Float:drift_grip = get_pcvar_float(g_pc_drift_grip);
        new Float:drag = get_pcvar_float(g_pc_drag);

        // Gas and braking.
        if (buttons & IN_FORWARD)
        {
            if (speed < 0.0)
                speed += brake * DT;
            else
                speed += accel * DT;
        }
        else if (buttons & IN_BACK)
        {
            if (speed > 0.0)
                speed -= brake * DT;
            else
                speed -= accel * DT;
        }
        else
        {
            speed *= drag;

            if (floatabs(speed) < 2.0)
                speed = 0.0;
        }

        if (speed > maxspeed)
            speed = maxspeed;

        if (speed < -reverse)
            speed = -reverse;

        // Steering depends on speed. Reverse steering is inverted.
        new Float:steer = 0.0;

        if (buttons & IN_MOVELEFT)
            steer += 1.0;

        if (buttons & IN_MOVERIGHT)
            steer -= 1.0;

        new Float:speed_factor = floatabs(speed) / maxspeed;

        if (speed_factor > 1.0)
            speed_factor = 1.0;

        if (speed_factor < 0.08)
            speed_factor = 0.0;

        new Float:direction = 1.0;

        if (speed < 0.0)
            direction = -1.0;

        yaw += steer * steer_rate * speed_factor
             * direction * DT;

        new Float:angles[3];
        angles[0] = 0.0;
        angles[1] = yaw;
        angles[2] = 0.0;

        set_pev(ent, pev_angles, angles);

        // Desired forward velocity.
        engfunc(EngFunc_MakeVectors, angles);

        new Float:forward[3];
        global_get(glb_v_forward, forward);

        new Float:velocity[3];
        pev(ent, pev_velocity, velocity);

        // Keep the current lateral velocity, then damp it according
        // to normal grip or the reduced grip used while drifting.
        new Float:longitudinal =
            velocity[0] * forward[0] +
            velocity[1] * forward[1];

        new Float:side_x = velocity[0] - forward[0] * longitudinal;
        new Float:side_y = velocity[1] - forward[1] * longitudinal;

        new Float:active_grip = grip;

        if (buttons & IN_JUMP)
            active_grip = drift_grip;

        new Float:blend = active_grip * DT;

        if (blend > 1.0)
            blend = 1.0;

        side_x *= (1.0 - blend);
        side_y *= (1.0 - blend);

        velocity[0] = forward[0] * speed + side_x;
        velocity[1] = forward[1] * speed + side_y;

        // No artificial vertical propulsion.
        // MOVETYPE_FLY still does not provide wheel suspension.
        velocity[2] = 0.0;

        set_pev(ent, pev_velocity, velocity);
        set_pev(ent, pev_fuser1, speed);
        set_pev(ent, pev_fuser2, yaw);

        // Attach the player to the driver's seat.
        new Float:car_origin[3];
        pev(ent, pev_origin, car_origin);
        car_origin[2] += 12.0;

        engfunc(EngFunc_SetOrigin, driver, car_origin);

        // Align the player's view with the vehicle.
        new Float:view_angles[3];
        pev(driver, pev_v_angle, view_angles);

        view_angles[0] = 0.0;
        view_angles[1] = yaw;
        view_angles[2] = 0.0;

        set_pev(driver, pev_v_angle, view_angles);
        set_pev(driver, pev_angles, view_angles);
        set_pev(driver, pev_fixangle, 1);
    }
    else if (driver > 0)
    {
        if (is_user_connected(driver))
            exit_vehicle(driver, true);
        else
            g_driver[ent] = 0;
    }

    set_pev(ent, pev_nextthink, get_gametime() + DT);
}

public fw_touch(ent, other)
{
    if (ent <= 0 || ent >= MAX_EDICTS || !pev_valid(ent))
        return FMRES_IGNORED;

    static classname[32];
    pev(ent, pev_classname, classname, charsmax(classname));

    if (!equal(classname, VEH_CLASS))
        return FMRES_IGNORED;

    if (other > 0 && pev_valid(other))
    {
        new Float:velocity[3], Float:speed;

        pev(ent, pev_velocity, velocity);
        pev(ent, pev_fuser1, speed);

        // Arcade impact damping.
        velocity[0] *= 0.60;
        velocity[1] *= 0.60;
        speed *= 0.60;

        set_pev(ent, pev_velocity, velocity);
        set_pev(ent, pev_fuser1, speed);
    }

    return FMRES_IGNORED;
}