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



#include <amxmodx>
#include <engine>
#include <fakemeta>
#include <xs>

#define PLUGIN  "VC Arcade Vehicles"
#define VERSION "0.1"
#define AUTHOR  "OpenAI"

#define VEHICLE_CLASS "vc_vehicle"

#define IN_FORWARD  (1<<3)
#define IN_BACK     (1<<4)
#define IN_USE      (1<<5)
#define IN_MOVELEFT (1<<9)
#define IN_MOVERIGHT (1<<10)

#define FL_FROZEN (1<<5)

new g_vehicle[33];
new g_driver[2049];
new g_saved_movetype[33];
new g_saved_solid[33];
new bool:g_was_frozen[33];

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

new Float:g_accel = 360.0;
new Float:g_brake = 600.0;
new Float:g_max_speed = 750.0;
new Float:g_reverse_speed = 220.0;
new Float:g_drag = 0.985;
new Float:g_steer = 100.0;
new Float:g_lateral_grip = 5.0;

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

    register_clcmd("say /car", "cmd_spawn_car");
    register_clcmd("say /exit", "cmd_exit_car");

    register_forward(FM_PlayerPreThink, "fw_PlayerPreThink");
    register_forward(FM_Touch, "fw_Touch");

    register_think(VEHICLE_CLASS, "vehicle_think");
}

public plugin_precache()
{
    precache_model(g_model);
}

public client_putinserver(id)
{
    g_vehicle[id] = 0;
}

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

public cmd_spawn_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)
        return PLUGIN_HANDLED;

    new Float:origin[3], Float:angles[3];
    pev(id, pev_origin, origin);
    pev(id, pev_v_angle, angles);

    new Float:forward[3];
    angle_vector(angles, ANGLEVECTOR_FORWARD, forward);

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

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

    entity_set_string(ent, EV_SZ_classname, VEHICLE_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 = current 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() + 0.02);

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

    return PLUGIN_HANDLED;
}

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

    new ent = g_vehicle[id];

    if (ent > 0 && pev_valid(ent))
    {
        new buttons = pev(id, pev_button);

        // E exits the vehicle. Clear IN_USE to avoid repeated exits.
        if ((buttons & IN_USE) && !(pev(id, pev_oldbuttons) & IN_USE))
        {
            exit_vehicle(id, true);
            return FMRES_IGNORED;
        }

        // Vehicle movement is handled by vehicle_think().
        set_pev(id, pev_velocity, Float:{0.0, 0.0, 0.0});
        return FMRES_IGNORED;
    }

    // Find the nearest vehicle when E is pressed.
    new buttons = pev(id, pev_button);
    new oldbuttons = pev(id, pev_oldbuttons);

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

        pev(id, pev_origin, player_origin);

        while (ent2 > 0)
        {
            pev(ent2, pev_origin, car_origin);

            if (get_distance_f(player_origin, car_origin) < 110.0
                && g_driver[ent2] == 0)
            {
                enter_vehicle(id, ent2);
                break;
            }

            ent2 = find_ent_by_class(ent2, VEHICLE_CLASS);
        }
    }

    return FMRES_IGNORED;
}

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

    g_saved_movetype[id] = pev(id, pev_movetype);
    g_saved_solid[id] = pev(id, pev_solid);
    g_was_frozen[id] = (pev(id, pev_flags) & FL_FROZEN) != 0;

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

    client_print(id, print_center,
        "W/S - accelerate/brake | A/D - steer | E - exit");
}

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

    if (ent <= 0)
        return;

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

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

        angle_vector(angles, ANGLEVECTOR_RIGHT, right);

        origin[0] += right[0] * 65.0;
        origin[1] += right[1] * 65.0;
        origin[2] += 25.0;

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

        g_driver[ent] = 0;
    }

    g_vehicle[id] = 0;

    set_pev(id, pev_movetype, g_saved_movetype[id]);
    set_pev(id, pev_solid, g_saved_solid[id]);

    if (g_was_frozen[id])
    {
        new flags = pev(id, pev_flags);
        set_pev(id, pev_flags, flags | FL_FROZEN);
    }
    else
    {
        new flags = pev(id, pev_flags);
        set_pev(id, pev_flags, flags & ~FL_FROZEN);
    }

    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);

        // Acceleration and braking.
        if (buttons & IN_FORWARD)
        {
            speed += g_accel * 0.02;
        }
        else if (buttons & IN_BACK)
        {
            if (speed > 0.0)
                speed -= g_brake * 0.02;
            else
                speed -= g_accel * 0.02;
        }
        else
        {
            speed *= g_drag;
        }

        if (speed > g_max_speed)
            speed = g_max_speed;

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

        // Steering fades at very low speed.
        new Float:steering = 0.0;

        if (buttons & IN_MOVELEFT)
            steering += 1.0;

        if (buttons & IN_MOVERIGHT)
            steering -= 1.0;

        new Float:steer_factor = floatabs(speed) / g_max_speed;

        if (steer_factor > 1.0)
            steer_factor = 1.0;

        yaw += steering * g_steer * steer_factor * 0.02;

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

        set_pev(ent, pev_angles, angles);

        new Float:forward[3], Float:velocity[3];
        angle_vector(angles, ANGLEVECTOR_FORWARD, forward);

        pev(ent, pev_velocity, velocity);

        // Arcade lateral grip: suppress sideways sliding.
        new Float:forward_speed =
            velocity[0] * forward[0] +
            velocity[1] * forward[1];

        new Float:target_x = forward[0] * speed;
        new Float:target_y = forward[1] * speed;

        velocity[0] += (target_x - velocity[0])
                       * g_lateral_grip * 0.02;
        velocity[1] += (target_y - velocity[1])
                       * g_lateral_grip * 0.02;

        velocity[2] = 0.0;

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

        // Keep the driver attached above the vehicle.
        new Float:origin[3];
        pev(ent, pev_origin, origin);
        origin[2] += 24.0;

        engfunc(EngFunc_SetOrigin, driver, origin);

        new Float:view_angles[3];
        pev(driver, pev_v_angle, view_angles);
        view_angles[1] = yaw;
        set_pev(driver, pev_angles, view_angles);
        set_pev(driver, pev_fixangle, 1);
    }
    else if (driver > 0)
    {
        g_driver[ent] = 0;
    }

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

public fw_Touch(ent, other)
{
    if (!pev_valid(ent))
        return FMRES_IGNORED;

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

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

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

        // Basic arcade collision damping.
        velocity[0] *= 0.55;
        velocity[1] *= 0.55;

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

    return FMRES_IGNORED;
}

stock angle_vector(const Float:angles[3], mode, Float:output[3])
{
    new Float:forward[3], Float:right[3], Float:up[3];

    engfunc(EngFunc_AngleVectors, angles, forward, right, up);

    if (mode == ANGLEVECTOR_FORWARD)
        xs_vec_copy(forward, output);
    else if (mode == ANGLEVECTOR_RIGHT)
        xs_vec_copy(right, output);
    else
        xs_vec_copy(up, output);
}