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


#include < amxmisc >
#include < fakemeta >
#include < reapi >
#include < json >

#define MAX_SPAWN_POINTS 128
#define INVALID_SPAWN -1

enum _: SpawnData
{
    Float: data_origin[ 3 ],
    Float: data_v_angle[ 3 ]
};

new Array: m_aSpawnPointsVec,
    Array: m_aDefaultSpawnPointsVec;

new m_iLastSpawnId = INVALID_SPAWN,
    m_iSpawnCount,
    m_iDefaultSpawnCount,
    bool: m_bUsingCustomSpawns;

new m_szMapName[ 32 ],
    m_szFilePath[ PLATFORM_MAX_PATH ];

public plugin_precache( )
{
    register_plugin( "Spawn points", "0.0.1", "oxsi. x Vision" );
}

public plugin_init( )
{
    m_aSpawnPointsVec = ArrayCreate( SpawnData );
    m_aDefaultSpawnPointsVec = ArrayCreate( );

    RegisterHookChain( RG_CSGameRules_GetPlayerSpawnSpot, "@CSGameRules_GetPlayerSpawnSpot_Pre", false );

    register_clcmd( "say /spawns", "@Open_SpawnPoints_Menu" );
    register_menucmd( register_menuid( "@Display_SpawnPoints_Menu" ), ( 1 << 0 | 1 << 1 | 1 << 9 ), "@Handler_SpawnPoints_Menu" );

    @Load_CustomSpawnPoints( );
    if ( m_iSpawnCount > 0 )
    {
        @Active_CustomSpawnPoints( );
        m_iDefaultSpawnCount = ArraySize( m_aDefaultSpawnPointsVec );
    }

    if ( !m_bUsingCustomSpawns )
        @Load_DefaultSpawnPoints( );

    server_print( "Map: %s | Points: %d | Custom: %s", m_szMapName, m_iSpawnCount, m_bUsingCustomSpawns ? "yes" : "no" );
}

public plugin_end( )
{
    if ( m_aSpawnPointsVec != Invalid_Array )
        ArrayDestroy( m_aSpawnPointsVec );

    if ( m_aDefaultSpawnPointsVec != Invalid_Array )
        ArrayDestroy( m_aDefaultSpawnPointsVec );
}

@Open_SpawnPoints_Menu( id )
{
    return @Display_SpawnPoints_Menu( id );
}

@Display_SpawnPoints_Menu( id )
{
    new menu[ 512 ], key = ( 1 << 9 ),
    len = add( menu, charsmax( menu ), fmt( "\ySpawn points %d^n^n", m_iSpawnCount ) );

    add( menu[ len ], charsmax( menu ) - len, "\y1. \wAdd points at current position.^n" );
    key |= ( 1 << 0 );

    add( menu[ len ], charsmax( menu ) - len, "^n^n\y0. \wExit" );
    return show_menu( id, key, menu, -1, "@Display_SpawnPoints_Menu" );
}

@Handler_SpawnPoints_Menu( id, key )
{
    switch ( key )
    {
        case 0:
        {
            @Add_SpawnPoint( id );
            @Display_SpawnPoints_Menu( id );
        }
        case 9: return;
    }
}

@CSGameRules_GetPlayerSpawnSpot_Pre( id )
{
    if ( TEAM_TERRORIST > get_member( id, m_iTeam ) > TEAM_CT )
        return HC_CONTINUE;

    new spot = @Select_SpawnPoint( id );
    if ( is_nullent( spot ) )
        return HC_CONTINUE;

    new Float: origin[ 3 ], Float: angles[ 3 ], Float: v_angle[ 3 ];
    get_entvar( spot, var_origin, origin );
    get_entvar( spot, var_angles, angles );
    get_entvar( spot, var_v_angle, v_angle );

    origin[ 2 ] += 1.0;

    set_entvar( id, var_origin, origin );
    set_entvar( id, var_angles, angles );
    set_entvar( id, var_v_angle, v_angle );
    set_entvar( id, var_velocity, NULL_VECTOR );
    set_entvar( id, var_punchangle, NULL_VECTOR );
    set_entvar( id, var_fixangle, 1 );

    SetHookChainReturn( ATYPE_INTEGER, spot );
    return HC_SUPERCEDE;
}

@Select_SpawnPoint( id )
{
    new count = ArraySize( m_aDefaultSpawnPointsVec );
    if ( count <= 0 )
        return 0;

    new spot, Float: origin[ 3 ];
    for ( new attempt = 0; attempt < count; attempt++ )
    {
        m_iLastSpawnId++;
        if ( m_iLastSpawnId >= count )
            m_iLastSpawnId = 0;

        spot = ArrayGetCell( m_aDefaultSpawnPointsVec, m_iLastSpawnId );
        if ( is_nullent( spot ) )
            continue;

        get_entvar( spot, var_origin, origin );
        if ( !@is_hull_vacant( id, origin ) )
            continue;

        return spot;
    }

    return 0;
}

@Load_CustomSpawnPoints( )
{
    get_mapname( m_szMapName, charsmax( m_szMapName ) );

    new cfgsDir[ PLATFORM_MAX_PATH ];
    get_localinfo( "amxx_configsdir", cfgsDir, charsmax( cfgsDir ) );
    formatex( m_szFilePath, charsmax( m_szFilePath ), "%s/spawns.json", cfgsDir );
    if ( !file_exists( m_szFilePath ) )
        return;

    new JSON: root = json_parse( m_szFilePath, true );
    if ( root == Invalid_JSON || !json_is_object( root ) )
    {
        if ( root != Invalid_JSON )
            json_free( root );

        set_fail_state( "Parsing file '%s' error.", m_szFilePath );
        return;
    }

    new JSON: points = json_object_get_value( root, m_szMapName );
    if ( points == Invalid_JSON || !json_is_object( points ) )
    {
        if ( points != Invalid_JSON )
            json_free( points );

        json_free( root );
        return;
    }

    new count = json_array_get_count( points );
    for ( new i = 0; i < count && i < MAX_SPAWN_POINTS; i++ )
    {
        new JSON: point = json_array_get_value( points, i );
        if ( point == Invalid_JSON || !json_is_object( point ) )
        {
            if ( point != Invalid_JSON )
                json_free( point );

            continue;
        }

        new JSON: origin = json_object_get_value( point, "origin" );
        new JSON: v_angle = json_object_get_value( point, "v_angle" );
        if ( origin == Invalid_JSON || v_angle == Invalid_JSON || !json_is_array( origin ) || !json_is_array( v_angle ) || json_array_get_count( origin ) < 3 || json_array_get_count( v_angle ) < 3 )
        {
            json_free( origin );
            json_free( v_angle );
            json_free( point );
            continue;
        }

        new data[ SpawnData ];
        for ( new axis = 0; axis < 3; axis++ )
        {
            data[ data_origin ][ axis ] = json_array_get_real( origin, axis );
            data[ data_v_angle ][ axis ] = json_array_get_real( v_angle, axis );
        }

        ArrayPushArray( m_aSpawnPointsVec, data );
        m_iSpawnCount = ArraySize( m_aSpawnPointsVec );

        json_free( origin );
        json_free( v_angle );
        json_free( point );
    }

    json_free( points );
    json_free( root );
}

bool: @Save_CustomSpawnPoints( )
{
    new JSON: root;
    if ( file_exists( m_szFilePath ) )
        root = json_parse( m_szFilePath, true );
    else
        root = Invalid_JSON;

    if ( root == Invalid_JSON || !json_is_object( root ) )
    {
        if ( root != Invalid_JSON )
            json_free( root );

        root = json_init_object( );
    }

    if ( m_iSpawnCount == 0 )
        json_object_remove( root, m_szMapName );
    else
    {
        new JSON: points = json_init_array( );
        for ( new i = 0; i < m_iSpawnCount; i++ )
        {
            new data[ SpawnData ];
            ArrayGetArray( m_aSpawnPointsVec, i, data );

            new JSON: point = json_init_object( );
            new JSON: origin = json_init_array( );
            new JSON: v_angle = json_init_array( );

            for ( new axis = 0; axis < 3; axis++ )
            {
                json_array_append_real( origin, data[ data_origin ][ axis ] );
                json_array_append_real( v_angle, data[ data_v_angle ][ axis ] );
            }

            json_object_set_value( point, "origin", origin );
            json_object_set_value( point, "v_angle", v_angle );
            json_array_append_value( points, point );

            json_free( origin );
            json_free( v_angle );
            json_free( point );
        }

        json_object_set_value( root, m_szMapName, points );
        json_free( points );
    }

    new bool: succes = json_serial_to_file( root, m_szFilePath, true );
    json_free( root );

    if ( !succes )
        log_amx( "Failed to write %s", m_szFilePath );

    return succes;
}

@Load_DefaultSpawnPoints( )
{
    new entity = NULLENT;
    while ( ( entity = rg_find_ent_by_class( entity, "info_player_start", true ) ) > 0 )
        ArrayPushCell( m_aDefaultSpawnPointsVec, entity );

    entity = NULLENT;
    while ( ( entity = rg_find_ent_by_class( entity, "info_player_deathmatch", true ) ) > 0 )
        ArrayPushCell( m_aDefaultSpawnPointsVec, entity );

    m_bUsingCustomSpawns = false;
}

bool: @Active_CustomSpawnPoints( )
{
    for ( new i = 0; i < m_iSpawnCount; i++ )
    {
        new data[ SpawnData ];
        ArrayGetArray( m_aSpawnPointsVec, i, data );

        new entity = rg_create_entity( "info_player_deathmatch" );
        if ( is_nullent( entity ) )
            continue;

        set_entvar( entity, var_origin, data[ data_origin ] );
        set_entvar( entity, var_angles, data[ data_v_angle ] );

        dllfunc( DLLFunc_Spawn, entity );
        if ( is_nullent( entity ) )
            continue;

        ArrayPushCell( m_aDefaultSpawnPointsVec, entity );
    }

    m_iDefaultSpawnCount = ArraySize( m_aDefaultSpawnPointsVec );
    if ( m_iDefaultSpawnCount > 0 )
    {
        m_bUsingCustomSpawns = true;
        return true;
    }

    return false;
}

@Add_SpawnPoint( id )
{
    if ( !is_user_alive( id ) )
    {
        client_print( id, print_chat, "You can't add a spawn point." );
        return;
    }

    if ( m_iSpawnCount >= MAX_SPAWN_POINTS )
    {
        client_print( id, print_chat, "Maximum number of spawn points reached." );
        return;
    }

    new data[ SpawnData ], Float: angles[ 3 ];
    get_entvar( id, var_origin, data[ data_origin ] );
    get_entvar( id, var_angles, angles );
    get_entvar( id, var_v_angle, data[ data_v_angle ] );

    ArrayPushArray( m_aSpawnPointsVec, data );
    m_iSpawnCount = ArraySize( m_aSpawnPointsVec );

    if ( !@Save_CustomSpawnPoints( ) )
    {
        ArrayDeleteItem( m_aSpawnPointsVec, ArraySize( m_aSpawnPointsVec ) - 1 );
        m_iSpawnCount = ArraySize( m_aSpawnPointsVec );

        client_print( id, print_chat, "Save failed." );
        return;
    }

    client_print( id, print_chat, "Point saved. It will activate next map." );
}

bool: @is_hull_vacant( id, Float: origin[ 3 ] )
{
    engfunc( EngFunc_TraceHull, origin, origin, 0, HULL_HUMAN, id, 0 );
    return !get_tr2( 0, TR_StartSolid ) && !get_tr2( 0, TR_AllSolid ) && get_tr2( 0, TR_InOpen );
}