Автоматическая расстановка спавнов

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
Для полноценного плагина, пожалуй, не годится, но как наработка сойдёт.
У меня есть идея фикс - написать автоматическую расстановку спавнов по карте для таких модов как gungame, csdm. Знаю, что это довольно сложно, поэтому решил начать с малого - поставил перед собой задачу написать плагин, который будет автоматически добавлять спавны, если на карте их изначально меньше 16 (cs_assault, cs_militia, cs_estate, de_nuke и т.д.). Знаете, даже кое что получилось.

1. Для начала подсчитал количество спавнов, определил сколько нужно добавить. Записал ID и координаты каждого спавна.
1634084949175.png
2. Для наглядности поставил модельки
1634086229988.png
3. Прошёлся циклом по полученным спавнам, вокруг каждого из них искал свободное место по такой схеме:
1634085965573.png
Попутно выводил в консоль разную информацию:
  • номер каждого проверяемого спавна
  • наличие энтити вокруг этого спавна
  • в случае успеха - информацию о новом созданном спавне
1634086016412.png
4. Так же поставил модельки на новых созданных спавнах
1634086391258.png
5. Проверил заново количество спавнов
1634086517941.png
Результат меня на данном этапе вполне устраивает. Проверял на таких картах: cs_assault, cs_office, cs_italy, cs_estate, cs_militia (эти карты имеют по 10 слотов на команду, de_ карты в основном 16-20 слотов) , на парочке своих карт, где специально расставлял 8-10 спавнов среди различных брашей и энтити.
Вот что вышло.
1634086836462.png
На cs_assault некоторые новые спавны зависли в воздухе, поэтому надо будет добавить проверку на расстояние до "пола", где то видел тут на форуме нужный сток.
1634086893045.png1634087004226.png1634087062432.png
Собственно, сам код:
Код:
#include <amxmodx>
#include <reapi>
#include <engine>
#include <fakemeta>

#define TT_MDL    "models/player/leet/leet.mdl"
#define CT_MDL    "models/player/gign/gign.mdl"

new g_iModel_ct, g_iModel_tt;
new g_SpawnsNum[3];
new g_SpawnsNumNeeded[3];
new g_EntCT[32], g_EntTT[32];
new Float:coordsCT[32][3];
new Float:coordsTT[32][3];
new const Float:point[8][2] = {    {0.0, -1.0}        ,     // we will try to find space in 2D
                                {1.0, -1.0}        ,     // 8 point around each spawn
                                {1.0, 0.0}        ,    // in all X Y directions
                                {1.0, 1.0}        ,   
                                {0.0, 1.0}        ,
                                {-1.0, 1.0}        ,
                                {0.0, -1.0}        ,
                                {-1.0, -1.0}    };

public plugin_init(){
    get_spawns_num();
  
    register_plugin("ASP", "1.0.0", "iPlague");
    register_clcmd("test", "test_spawns");
    register_srvcmd("test", "test_spawns");
  
    register_clcmd("check", "check_spawns");
    register_clcmd("check_tt_spawn", "check_tt_spawn");
    register_clcmd("check_ct_spawn", "check_ct_spawn");
}

public plugin_precache(){
    g_iModel_ct = precache_model(CT_MDL);
    g_iModel_tt = precache_model(TT_MDL);
}

get_spawns_num(){
    server_print("================= COUNT SPAWNS =================");
    new Ent1 = NULLENT, numCT = 0;
    while((Ent1 = rg_find_ent_by_class(Ent1, "info_player_start")) != 0){
        g_EntCT[numCT] = Ent1;
        new Float:forigin1[3]; get_entvar(g_EntCT[numCT], var_origin, forigin1);

        set_entvar(g_EntCT[numCT], var_model, CT_MDL);
        set_entvar(g_EntCT[numCT], var_modelindex, g_iModel_ct);
      
        coordsCT[numCT][0] = forigin1[0];
        coordsCT[numCT][1] = forigin1[1];
        coordsCT[numCT][2] = forigin1[2];
        server_print("%d. CT spawn ID: %d | Origins: %.0f %.0f %.0f", numCT + 1, g_EntCT[numCT], forigin1[0],forigin1[1],forigin1[2]);
        numCT++;
    }
    Ent1 = NULLENT; new numTT = 0;
    while((Ent1 = rg_find_ent_by_class(Ent1, "info_player_deathmatch")) != 0){    // Ищем спавны ТТ, записываем координаты, пишем инфу в консоль.
        g_EntTT[numTT] = Ent1;
        new Float:forigin1[3]; get_entvar(g_EntTT[numTT], var_origin, forigin1);

        set_entvar(g_EntTT[numTT], var_model, TT_MDL);
        set_entvar(g_EntTT[numTT], var_modelindex, g_iModel_tt);
              
        coordsTT[numTT][0] = forigin1[0];
        coordsTT[numTT][1] = forigin1[1];
        coordsTT[numTT][2] = forigin1[2];
        server_print("%d. TT spawn ID: %d | Origins: %.0f %.0f %.0f", numTT + 1, g_EntTT[numTT], forigin1[0],forigin1[1],forigin1[2]);
        numTT++;
    }
    g_SpawnsNum[1] = numTT; g_SpawnsNum[2] = numCT;
    g_SpawnsNumNeeded[1] = max(16 - g_SpawnsNum[1], 0);
    g_SpawnsNumNeeded[2] =  max(16 - g_SpawnsNum[2], 0);
    server_print("Totaly found CT spawns: %d | TT spawns: %d", g_SpawnsNum[1], g_SpawnsNum[2]);
    server_print("SO THAT WE NEED TO ADD %d CT & %d TT SPAWNS", g_SpawnsNumNeeded[1], g_SpawnsNumNeeded[2]);
}

public test_spawns(){
    server_print("================= LETS TRY =================");
  
    static Float:mins[3],Float:vec[3];
    mins[0] = mins[1] = -24.0;
    mins[2] = -40.0;
    new num = g_SpawnsNum[1];
    for(new i = 0; i < num; i++){// for each TT spawn
        if(g_SpawnsNumNeeded[1] <= 0){
            server_print("WE ALREADY DON'T NEED TO ADD TT SPAWNS");
            server_print("================== FINISH ====================");
            break;
        }
      
        server_print("TRY TO FIND FREE SPACE AROUND %dth SPAWN^n", i + 1);
      
        vec[2] = coordsTT[i][2];
        for (new j; j < sizeof point; j++){    // for each direction check free space around %i spawn
            vec[0] = coordsTT[i][0] - 6.0 * mins[0] * point[j][0];
            vec[1] = coordsTT[i][1] - 6.0 * mins[1] * point[j][1];
          
            if(is_place_ok(vec, 48.0) && is_place_in_side(g_EntTT[i], coordsTT[i], vec)){
                server_print("WE ADD 1 SPAWN AROUND %dth TT SPAWN^n", i+1);
                new ent_T = rg_create_entity("info_player_deathmatch", false);
                //if(!is_nullent(ent_T)){
                set_entvar(ent_T, var_model, TT_MDL);
                set_entvar(ent_T, var_modelindex, g_iModel_tt);
                engfunc(EngFunc_SetOrigin, ent_T, vec);
                set_entvar(ent_T, var_renderfx, kRenderFxGlowShell);
                set_entvar(ent_T, var_rendercolor, {255.0,0.0,0.0});
                set_entvar(ent_T, var_rendermode, kRenderNormal);
                set_entvar(ent_T, var_renderamt, 32);
                g_SpawnsNumNeeded[1]--;
              
                g_SpawnsNum[1]++;
                g_EntTT[g_SpawnsNum[1] - 1] = ent_T;
                coordsTT[g_SpawnsNum[1] - 1][0] = vec[0];
                coordsTT[g_SpawnsNum[1] - 1][1] = vec[1];
                coordsTT[g_SpawnsNum[1] - 1][2] = vec[2];
              
                break; // for the 1th time we try to create only one spawn around each point
                //}
            }
        }
    }
    num = g_SpawnsNum[2];
    for(new i = 0; i < num; i++){// for each CT spawn
        if(g_SpawnsNumNeeded[2] <= 0){
            server_print("WE ALREADY DON'T NEED TO ADD CT SPAWNS");
            server_print("================== FINISH ====================");
            break;
        }
      
        server_print("TRY TO FIND FREE SPACE AROUND %dth SPAWN^n", i + 1);
      
        vec[2] = coordsCT[i][2];
        for (new j; j < sizeof point; j++){    // for each direction check free space around %i spawn
            vec[0] = coordsCT[i][0] - 6.0 * mins[0] * point[j][0];
            vec[1] = coordsCT[i][1] - 6.0 * mins[1] * point[j][1];
          
            if(is_place_ok(vec, 48.0) && is_place_in_side(g_EntCT[i], coordsCT[i], vec)){
                server_print("WE ADD 1 SPAWN AROUND %dth CT SPAWN^n", i+1);
                new ent_CT = rg_create_entity("info_player_start", false);
                //if(!is_nullent(ent_CT)){
                set_entvar(ent_CT, var_model, CT_MDL);
                set_entvar(ent_CT, var_modelindex, g_iModel_ct);
                engfunc(EngFunc_SetOrigin, ent_CT, vec);
                set_entvar(ent_CT, var_renderfx, kRenderFxGlowShell);
                set_entvar(ent_CT, var_rendercolor, {0.0,0.0,255.0});
                set_entvar(ent_CT, var_rendermode, kRenderNormal);
                set_entvar(ent_CT, var_renderamt, 32);
                g_SpawnsNumNeeded[2]--;
              
                g_SpawnsNum[2]++;
                g_EntCT[g_SpawnsNum[2] - 1] = ent_CT;
                coordsCT[g_SpawnsNum[2] - 1][0] = vec[0];
                coordsCT[g_SpawnsNum[2] - 1][1] = vec[1];
                coordsCT[g_SpawnsNum[2] - 1][2] = vec[2];
  
                break; // for the 1th time we try to create only one spawn around each point
                //}
            }
        }
    }
}

public check_spawns(id){

    new Ent1 = NULLENT, numCT = 0, numTT = 0;
    while((Ent1 = rg_find_ent_by_class(Ent1, "info_player_start")) != 0)
        numCT++;

    while((Ent1 = rg_find_ent_by_class(Ent1, "info_player_deathmatch")) != 0)
        numTT++;
      
    server_print("RESULT: WE FOUND %d TTs AND %d CTs SPAWNS", numCT, numCT);
}

new check_tt = 0;
public check_tt_spawn(id){
    set_entvar(id, var_origin, coordsTT[check_tt]);
    server_print("WE AT %d spawn", check_tt);
  
    if(is_place_ok(coordsTT[check_tt], 48.0))
        server_print("SPAWN %d IS OK", check_tt);
  
    check_tt = (check_tt + 1) % (g_SpawnsNum[1]);
}

new check_ct = 0;
public check_ct_spawn(id){
    set_entvar(id, var_origin, coordsCT[check_ct]);
  
    server_print("WE AT %d spawn", check_ct);
  
    if(is_place_ok(coordsCT[check_ct], 48.0))
        server_print("SPAWN %d IS OK", check_tt);
  
    check_ct = (check_ct + 1) % (g_SpawnsNum[2]);
}

stock bool:is_place_in_side(idUser, const Float:start[3], const Float:dest[3]){
    engfunc(EngFunc_TraceLine, start, dest, 0, idUser, 0);
    new Float:fraction;    get_tr2(0, TR_flFraction, fraction);
    if(fraction == 1.0)
        return true;
    return false;
}

stock bool:is_place_ok(const Float:forigin[3], Float:radius){
    new Ent = NULLENT;
    while((Ent = find_ent_in_sphere(Ent, forigin, radius)) != 0){
        new szClassname[32]; get_entvar(Ent, var_classname, szClassname, 31);
        if(equal(szClassname, "func_buyzone") || equal(szClassname, "func_hostage_rescue"))
            continue;
        server_print("WE FIND %s entity in radius %.0f units", szClassname, radius)
        return false;
    }
    new tr = 0;    engfunc(EngFunc_TraceHull, forigin,  forigin, 0, HULL_HUMAN, 0, tr);
    if(!get_tr2(tr, TR_StartSolid) && !get_tr2(tr, TR_AllSolid) && get_tr2(tr, TR_InOpen))
        return true;
    return false;
}
Следующие этапы:
  • пофиксить баги, которые удастся найти
  • потестить на разных "сложных" картах
  • попробовать расстановку спавнов по всей площади для симметричных карт, начну с аim_map.

Спасибо за внимание.
 

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
Pokemoshka, спасибо! знач возьму полезное и хорошее откуда и буду дальше делать.
 
Сообщения
1,549
Реакции
1,555
Помог
2 раз(а)
iPlague,
+ но,как по мне - лучше это всё делать вручную.
Да,ты потратишь много времени на все свои карты,если их будет порядка свыше 30 штук,но зато - это будет расстановка в нужных и правильных тебе местах
 
Сообщения
673
Реакции
242
Помог
11 раз(а)
Тут получается расставляется спавны не рандомно а только близко к своей спавны?


Предложение: генерировать создание спавн не зависимости от команды
 

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
Limbooc, расстановка идёт вокруг уже существующих спавнов, не рандомно а по схеме.
13 Окт 2021
Предложение: генерировать создание спавн не зависимости от команды
это как? рандомно по карте? если да - то работаю над алгоритмом. не знаю, получится ли, но попробовать хочу.
 
Сообщения
775
Реакции
293
Помог
11 раз(а)
iPlague, ну тут тоже бы как-то в углах, на ящиках или тому подобное
 
Сообщения
673
Реакции
242
Помог
11 раз(а)
Limbooc, расстановка идёт вокруг уже существующих спавнов, не рандомно а по схеме.
13 Окт 2021

это как? рандомно по карте? если да - то работаю над алгоритмом. не знаю, получится ли, но попробовать хочу.
один из вариантов
1) ну например, найти все объекты на карте с помощью find_ent_by_class , исключить некоторые такие как байзоны, бомб планты, и на поверхности объект создать спавн
2) каждой rg_find_ent_by_class(Ent1, "info_player_deathmatch") сгенерировать спавн команд соотношением 1к1, т.е. сначала ТТ спавн потом рядом +- прибавить пару метров и создать КТ спавн с учетом каолизии стен
 

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
Limbooc, я немного по другому делаю алгоритм, как будет готово - сразу выложу сюда.
 
Сообщения
2,491
Реакции
2,794
Помог
61 раз(а)
@iPlague,подобное делали многые. Последним ватчина делал. Также на аленях несколько готовых вариантов.
 

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
кое что переделал, больше спасибо fl0wer за подсказки.
можно добавить много-много спавнов, пока что проверка в одной плоскости относительно каждого спавна, добавлю ещё проверку по вертикали, что бы можно было на мелких ящичках спавниться.
Код:
#define DEBUG
#define MAX_SPAWNS 20            // num of spawns we need for each team
#define SEARCH_RADIUS 128.0        // recommended distance between spawn points
1635774152279.png
Добавило 2 спавна вокруг существующего:1635774420645.png
1635774521515.png1635774547372.png
Можно экспериментировать, меняя радиус поиска.
Дальше добавлю поиск выше/ниже точки отсчёта.

PS для @dmitry_isakow, на de_vertigo норм всё добавило, это не самая сложная карта.
1635775417471.png
За кт тоже создало 10 новых спавнов, я хз как там стать что бы они все в скрин влезли, поэтому вот, циклом прогнал по спавнам, нигде не застрял и не умер: 1635775578127.png
PSS ни одна курочка не пострадала.
 
Последнее редактирование:
Сообщения
509
Реакции
111
Помог
16 раз(а)
@iPlague,кстате подобное если будет реализовано возможно будет проблема на картах где установлена зона закупки и игроки кто реснется за этой зоной не смогут ничего покупать,тоесть обычно если ее нет,то зона расширяется в зависимости от респаунов,вокруг респауна как бы область небольшая
 

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
Алексеич, ни одна курочка не пострадала.
1 Ноя 2021
r1nk0, проверил на cs_offise, cs_assaul, cs_militia, cs_estate, de_nuke, de_vertigo и ещё нескольких картах, везде спавны были созданы в пределах байзоны, радиус поиска если поставить 64-72, то спавны будут плотненько стоять, зато все в зоне.
Для таких карт как aim_ fy_ вообще норм, проверял примерно на десятке карт.
 

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
Немного исправил код, там ошибки были. Проверил созданные слоты на ботах. Норм :)
register_srvcmd("asp_status", "show_asp_status");
Показывает количество энтити info_player_deathmatch/info_player_start и m_iSpawnPointCount_Terrorist, m_iSpawnPointCount_CT
1635796067781.png
Код:
#include <amxmodx>
#include <reapi>
#include <engine>
#include <fakemeta>

//#define DEBUG
#define MAX_SPAWNS 32            // num of spawns we need for each team
#define SEARCH_RADIUS 128.0        // recommended distance between spawn points

#if defined DEBUG
    #define MDL "models/chick.mdl"
    new g_iModel;
    public plugin_precache()g_iModel = precache_model(MDL);
#endif

new Array:g_TTSpawn;
new Array:g_CTSpawn;
new g_iLastSpawnId[3];
new const Float:point[8][2] = {    {0.0, -1.0},     // we will try to find space in 2D
                                {1.0, -1.0},     // 8 point around each spawn
                                {1.0, 0.0},        // in all X Y directions
                                {1.0, 1.0},   
                                {0.0, 1.0},
                                {-1.0, 1.0},
                                {0.0, -1.0},
                                {-1.0, -1.0}};

public plugin_init(){
    register_plugin("ASP", "1.0.0", "iPlague");

    g_TTSpawn = ArrayCreate(1, 0);
    g_CTSpawn = ArrayCreate(1, 0);
  
    #if defined DEBUG
    register_srvcmd("start", "get_spawns_num");
    register_srvcmd("check", "set_chicks");
    register_clcmd("tt", "check_tt_spawn");
    register_clcmd("ct", "check_ct_spawn");
    #else
    RegisterHookChain(RG_CSGameRules_GetPlayerSpawnSpot, "@CSGameRules_GetPlayerSpawnSpot_Pre", false); // prevent spawning if place isn't vacant
    RegisterHookChain(RG_CSGameRules_RestartRound, "@CSGameRules_RestartRound_Post", true); // reset iSpawnPointCount
    get_spawns_num();
    #endif
    register_srvcmd("asp_status", "show_asp_status");
}

public show_asp_status(){
server_print("===============================================^n^nAUTO SPAWN POINTS^nENT NUM: %d | SPAWN POINTS NUM: %d^nENT NUM: %d | SPAWN POINTS NUM: %d^n===============================================^n^n",
ArraySize(g_TTSpawn),get_member_game(m_iSpawnPointCount_Terrorist), ArraySize(g_CTSpawn),get_member_game(m_iSpawnPointCount_CT))
}

#if defined DEBUG
public set_chicks(){
    for(new i = 0; i < ArraySize(g_TTSpawn); i++){
        new chick = rg_create_entity("info_player_deathmatch", false);
      
        new iSpawn = ArrayGetCell(g_TTSpawn, i);
        new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);

        set_entvar(chick, var_model, MDL);
        set_entvar(chick, var_modelindex, g_iModel);
        engfunc(EngFunc_SetOrigin, chick, fOrigin);
        set_entvar(chick, var_classname, "sexy_chick");
        set_entvar(chick, var_movetype, MOVETYPE_NONE);
        set_entvar(chick, var_solid, SOLID_NOT);
        set_entvar(chick, var_renderfx, kRenderFxGlowShell);
        set_entvar(chick, var_rendercolor, {255.0,0.0,0.0});
        set_entvar(chick, var_rendermode, kRenderNormal);
        set_entvar(chick, var_renderamt, 36);
        server_print("%d. CHICK ENTITY %d | TT SPAWN ENTITY %d", i+1, chick, iSpawn);
    }
    for(new i = 0; i < ArraySize(g_CTSpawn); i++){
        new chick = rg_create_entity("info_player_deathmatch", false);
      
        new iSpawn = ArrayGetCell(g_CTSpawn, i);
        new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);

        set_entvar(chick, var_model, MDL);
        set_entvar(chick, var_modelindex, g_iModel);
        engfunc(EngFunc_SetOrigin, chick, fOrigin);
        set_entvar(chick, var_classname, "sexy_chick");
        set_entvar(chick, var_movetype, MOVETYPE_NONE);
        set_entvar(chick, var_solid, SOLID_NOT);
        set_entvar(chick, var_renderfx, kRenderFxGlowShell);
        set_entvar(chick, var_rendercolor, {0.0,0.0,255.0});
        set_entvar(chick, var_rendermode, kRenderNormal);
        set_entvar(chick, var_renderamt, 36);
        server_print("%d. CHICK ENTITY %d | CT SPAWN ENTITY %d", i+1, chick, iSpawn);
    }
}

new check_tt = 0;
public check_tt_spawn(id){
    new iSpawn = ArrayGetCell(g_TTSpawn, check_tt);
    new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);
  
    set_entvar(id, var_origin, fOrigin);
    server_print("WE AT %d spawn", check_tt);

    check_tt = (check_tt + 1) % (ArraySize(g_TTSpawn));
}

new check_ct = 0;
public check_ct_spawn(id){
    new iSpawn = ArrayGetCell(g_CTSpawn, check_ct);
    new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);
  
    set_entvar(id, var_origin, fOrigin);
    server_print("WE AT %d spawn", check_ct);

    check_ct = (check_ct + 1) % (ArraySize(g_CTSpawn));
}
#endif

public get_spawns_num(){
    if(!ArraySize(g_TTSpawn)){
        new SpawnEnt = NULLENT;
        while ((SpawnEnt = rg_find_ent_by_class(SpawnEnt, "info_player_deathmatch"))!= 0){
            ArrayPushCell(g_TTSpawn, SpawnEnt);
            #if defined DEBUG
            server_print("%d. TT ENTITY %d", ArraySize(g_TTSpawn), SpawnEnt);
            #endif
        }
    }
    if(!ArraySize(g_CTSpawn)){
        new SpawnEnt = NULLENT;
        while ((SpawnEnt = rg_find_ent_by_class(SpawnEnt, "info_player_start"))!= 0){
            ArrayPushCell(g_CTSpawn, SpawnEnt);
            #if defined DEBUG
            server_print("%d CT ENTITY %d", ArraySize(g_CTSpawn), SpawnEnt);
            #endif
        }
    }
    #if defined DEBUG
    server_print("= = = = = = = = = = =^nOLD RESULT: %d TTs AND %d CTs SPAWNS", ArraySize(g_TTSpawn), ArraySize(g_CTSpawn));
    #endif
    make_new_spawns();
}

public make_new_spawns(){
    new iTTSpawnsNum = ArraySize(g_TTSpawn);
    new iCTSpawnsNum = ArraySize(g_CTSpawn);

    new iNeedTTSpawns = max(MAX_SPAWNS - iTTSpawnsNum, 0);
    new iNeedCTSpawns = max(MAX_SPAWNS - iCTSpawnsNum, 0);
  
    #if defined DEBUG
    server_print("NEED: %d TT^n= = = = = = = = =^nSTART SEARCH TT SPAWNS^n", iNeedTTSpawns);
    #endif
    if(iNeedTTSpawns > 0){
        for(new i = 0; i < iTTSpawnsNum; i++){
            if(iNeedTTSpawns <= 0)
                break;
              
            new iSpawn = ArrayGetCell(g_TTSpawn, i);
            new Float:fOrigin[3];
            get_entvar(iSpawn , var_origin, fOrigin);
            new Float:fNewOrigin[3];
            fNewOrigin[2] = fOrigin[2];
            for(new j = 0; j < 8; j++){
                if(iNeedTTSpawns <= 0)
                    break;
                fNewOrigin[0] = (fOrigin[0] + SEARCH_RADIUS * point[j][0]);
                fNewOrigin[1] = (fOrigin[1] + SEARCH_RADIUS * point[j][1]);
              
                if(is_place_ok(fNewOrigin, 48.0) && is_place_in_sight(iSpawn, fOrigin, fNewOrigin)){
                    new iNewSpawn = rg_create_entity("info_player_deathmatch", true);
                    if(!is_nullent(iNewSpawn)){
                        engfunc(EngFunc_SetOrigin, iNewSpawn, fNewOrigin);
                        ArrayPushCell(g_TTSpawn, iNewSpawn);
                        iNeedTTSpawns--;
                        iTTSpawnsNum++;
                    }
                }
            }
        }
    }
    #if defined DEBUG
    server_print("NEED: %d CT^n= = = = = = = = =^nSTART SEARCH CT SPAWNS^n", iNeedCTSpawns);
    #endif
    if(iNeedCTSpawns > 0){
        for(new i = 0; i < iCTSpawnsNum; i++){
            if(iNeedCTSpawns  <= 0)
                break;
            new iSpawn = ArrayGetCell(g_CTSpawn, i);
            #if defined DEBUG
            server_print("%d. SEARCHING AROUND ENTITY %d", i+1, iSpawn);
            #endif
            new Float:fOrigin[3]; get_entvar(iSpawn , var_origin, fOrigin);
            new Float:fNewOrigin[3];
            fNewOrigin[2] = fOrigin[2];
            for(new j = 0; j < 8; j++){
                if(iNeedCTSpawns <= 0)
                    break;
                fNewOrigin[0] = (fOrigin[0] + SEARCH_RADIUS * point[j][0]);
                fNewOrigin[1] = (fOrigin[1] + SEARCH_RADIUS * point[j][1]);
              
                if(is_place_ok(fNewOrigin, 48.0) && is_place_in_sight(iSpawn, fOrigin, fNewOrigin)){
                    new iNewSpawn = rg_create_entity("info_player_start", true);
                    if(!is_nullent(iNewSpawn)){
                        #if defined DEBUG
                        server_print("+++++++++++ADD SPAWN ENTITY %d (j %d)", iNewSpawn, j);
                        #endif
                        engfunc(EngFunc_SetOrigin, iNewSpawn, fNewOrigin);
                        ArrayPushCell(g_CTSpawn, iNewSpawn);
                        iNeedCTSpawns--;
                        iCTSpawnsNum++;
                    }
                }
            }
        }
    }
    #if defined DEBUG
    server_print("NEW RESULT: %d TTs AND %d CTs SPAWNS", ArraySize(g_TTSpawn), ArraySize(g_CTSpawn));
    #endif
    reset_spawns_num();
}

public reset_spawns_num(){
    if(ArraySize(g_TTSpawn))
        set_member_game(m_iSpawnPointCount_Terrorist, ArraySize(g_TTSpawn));
  
    if(ArraySize(g_CTSpawn))
        set_member_game(m_iSpawnPointCount_CT, ArraySize(g_CTSpawn));

    set_member_game(m_bLevelInitialized, true);
}


@CSGameRules_RestartRound_Post()
    reset_spawns_num();


@CSGameRules_GetPlayerSpawnSpot_Pre(id){
    new TeamName:team = get_member(id, m_iTeam);
    if (team != TEAM_TERRORIST && team != TEAM_CT)
        return HC_CONTINUE;

    new spot = EntSelectSpawnPoint(id, team);

    if (is_nullent(spot))
        return HC_CONTINUE;

    new Float:vecOrigin[3];    get_entvar(spot, var_origin, vecOrigin);
    new Float:vecAngles[3];    get_entvar(spot, var_angles, vecAngles);

    vecOrigin[2] += 1.0;

    set_entvar(id, var_origin, vecOrigin);
    set_entvar(id, var_v_angle, NULL_VECTOR);
    set_entvar(id, var_velocity, NULL_VECTOR);
    set_entvar(id, var_angles, vecAngles);
    set_entvar(id, var_punchangle, NULL_VECTOR);
    set_entvar(id, var_fixangle, 1);

    SetHookChainReturn(ATYPE_INTEGER, spot);
    return HC_SUPERCEDE;
}

EntSelectSpawnPoint(id, TeamName:team){
    new spotId = g_iLastSpawnId[_:team],spot,Float:vecOrigin[3];
    do{
        if (++spotId >= ArraySize(_:team == 1 ? g_TTSpawn : g_CTSpawn))
            spotId = 0;

        switch(team){
            case TEAM_TERRORIST: spot = ArrayGetCell(g_TTSpawn, spotId);
            case TEAM_CT: spot = ArrayGetCell(g_CTSpawn, spotId);
        }
        if (is_nullent(spot))
            continue;
        get_entvar(spot, var_origin, vecOrigin);
        if (!IsHullVacant(id, vecOrigin, HULL_HUMAN))
            continue;
        break;
    }
    while (spotId != g_iLastSpawnId[_:team]);
    if (is_nullent(spot))
        return 0;
    g_iLastSpawnId[_:team] = spotId;
    return spot;
}

stock bool:is_place_in_sight(iEnt, const Float:start[3], const Float:dest[3]){
    engfunc(EngFunc_TraceLine, start, dest, 0, iEnt, 0);
    new Float:fraction;    get_tr2(0, TR_flFraction, fraction);
    if(fraction == 1.0)
        return true;
    #if defined DEBUG
    server_print("------------plase isn't in signt");
    #endif
    return false;
}

stock bool:is_place_ok(Float:forigin[3], Float:radius){
    new Ent = NULLENT;
    while((Ent = find_ent_in_sphere(Ent, forigin, radius)) != 0){
        #if defined DEBUG
        new classname[32]; get_entvar(Ent, var_classname, classname, charsmax(classname));
        server_print("------------%s)", classname, j);
        #endif
        new szClassname[32]; get_entvar(Ent, var_classname, szClassname, 31);
        if(equal(szClassname, "func_buyzone") || equal(szClassname, "func_hostage_rescue") || equal(szClassname, "env_sprite"))
            continue;
        return false;
    }
    if(distance_to_ground(forigin) > 128.0){
        #if defined DEBUG
        server_print("------------point too high");
        #endif
        return false;
    }
      
    new tr = 0;    engfunc(EngFunc_TraceHull, forigin,  forigin, 0, HULL_HUMAN, 0, tr);
    if(!get_tr2(tr, TR_StartSolid) && !get_tr2(tr, TR_AllSolid) && get_tr2(tr, TR_InOpen))
        return true;
    #if defined DEBUG
    server_print("------------hull isn't vacant (j %d)", j);
    #endif
    return false;
}

stock Float:distance_to_ground(Float:start[3]){
    new Float:end[3];

    end[0] = start[0];
    end[1] = start[1];
    end[2] = start[2] - 9999.0;
  
    new ptr = create_tr2();
    engfunc(EngFunc_TraceHull, start, end, IGNORE_MONSTERS, HULL_HUMAN, 0, ptr);
    new Float:distance;
    get_tr2(ptr, TR_flFraction, distance);
    free_tr2(ptr);
    distance *= 9999.0;
    return distance;
}

bool:IsHullVacant(id, Float:vecOrigin[3], hull){
    engfunc(EngFunc_TraceHull, vecOrigin, vecOrigin, 0, hull, id, 0);
    if (get_tr2(0, TR_StartSolid) || get_tr2(0, TR_AllSolid) || !get_tr2(0, TR_InOpen))
        return false;
    return true;
}
1635795938825.png1635795964907.png
Так же пока что упразднил выставление angles при спавне.
Было бы классно, если бы этот код кто то посмотрел и подсказал какие были допущены ошибки, спасибо.
 
Последнее редактирование:

iPlague

♿️
Сообщения
230
Реакции
130
Помог
2 раз(а)
На карте aim_map были спавны, в которых игрок застревал. Долго не мог понять почему так, пока не дошло что это оригинальные спавны такие кривые, а не созданные.
Дописал кусочек кода, который удаляет неправильные спавны. Командой "check" можно всё проверить, удалённые спавны обозначатся красными/синими моделями.
Так же нашёл и исправил кучу своих же ошибок :)
Код:
#include <amxmodx>
#include <reapi>
#include <engine>
#include <fakemeta>

#define DEBUG
//#define CHECK_VIS                // safe mode vhen we check if new point is visible from start point
#define REPAIR                    // delete wrong spawns   (player seems to stuck)
#define MAX_SPAWNS 24            // num of spawns we need for each team
#define SEARCH_RADIUS 72.0        // recommended distance between spawn points

#if defined DEBUG
    #define MDL "models/chick.mdl"
    #define FLAG_MDL     "models/warcraft3/flag.mdl"
    #define TT_MDL        "models/player/leet/leet.mdl"
    #define CT_MDL        "models/player/gign/gign.mdl"
    new g_iModel, g_iModel2, g_iModeltt, g_iModelct;
    public plugin_precache(){
        g_iModel = precache_model(MDL);
        g_iModel2 = precache_model(FLAG_MDL);
        g_iModeltt = precache_model(TT_MDL);
        g_iModelct = precache_model(CT_MDL);
    }
#endif

new Array:g_TTSpawn;
new Array:g_CTSpawn;
new g_iLastSpawnId[3];
new const Float:point[8][2] = {    {0.0, -1.0},     // we will try to find space in 2D
                                {1.0, -1.0},     // 8 point around each spawn
                                {1.0, 0.0},        // in all X Y directions
                                {1.0, 1.0},    
                                {0.0, 1.0},
                                {-1.0, 1.0},
                                {0.0, -1.0},
                                {-1.0, -1.0}};

public plugin_init(){
    register_plugin("ASP", "1.0.0", "iPlague");

    g_TTSpawn = ArrayCreate(1, 0);
    g_CTSpawn = ArrayCreate(1, 0);
   
    #if defined DEBUG
    register_srvcmd("start", "get_spawns_num");
    register_srvcmd("check", "set_chicks");
    register_clcmd("tt", "check_tt_spawn");
    register_clcmd("ct", "check_ct_spawn");
    #else
    RegisterHookChain(RG_CSGameRules_GetPlayerSpawnSpot, "@CSGameRules_GetPlayerSpawnSpot_Pre", false); // prevent spawning if place isn't vacant
    RegisterHookChain(RG_CSGameRules_RestartRound, "@CSGameRules_RestartRound_Post", true); // reset iSpawnPointCount
    get_spawns_num();
    #endif
    register_srvcmd("asp_status", "show_asp_status");
}

public show_asp_status(){
server_print("===============================================^n^nAUTO SPAWN POINTS^nENT NUM: %d | SPAWN POINTS NUM: %d^nENT NUM: %d | SPAWN POINTS NUM: %d^n===============================================^n^n",
ArraySize(g_TTSpawn),get_member_game(m_iSpawnPointCount_Terrorist), ArraySize(g_CTSpawn),get_member_game(m_iSpawnPointCount_CT));
}

#if defined DEBUG
public set_chicks(){
    for(new i = 0; i < ArraySize(g_TTSpawn); i++){
        new chick = rg_create_entity("info_target", false);
       
        new iSpawn = ArrayGetCell(g_TTSpawn, i);
        new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);

        set_entvar(chick, var_model, MDL);
        set_entvar(chick, var_modelindex, g_iModel);
        engfunc(EngFunc_SetOrigin, chick, fOrigin);
        set_entvar(chick, var_classname, "sexy_chick");
        set_entvar(chick, var_movetype, MOVETYPE_NONE);
        set_entvar(chick, var_solid, SOLID_NOT);
        set_entvar(chick, var_renderfx, kRenderFxGlowShell);
        set_entvar(chick, var_rendercolor, {255.0,0.0,0.0});
        set_entvar(chick, var_rendermode, kRenderNormal);
        set_entvar(chick, var_renderamt, 36);
        server_print("%d. CHICK ENTITY %d | TT SPAWN ENTITY %d", i+1, chick, iSpawn);
    }
    for(new i = 0; i < ArraySize(g_CTSpawn); i++){
        new chick = rg_create_entity("info_target", false);
       
        new iSpawn = ArrayGetCell(g_CTSpawn, i);
        new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);

        set_entvar(chick, var_model, MDL);
        set_entvar(chick, var_modelindex, g_iModel);
        engfunc(EngFunc_SetOrigin, chick, fOrigin);
        set_entvar(chick, var_classname, "sexy_chick");
        set_entvar(chick, var_movetype, MOVETYPE_NONE);
        set_entvar(chick, var_solid, SOLID_NOT);
        set_entvar(chick, var_renderfx, kRenderFxGlowShell);
        set_entvar(chick, var_rendercolor, {0.0,0.0,255.0});
        set_entvar(chick, var_rendermode, kRenderNormal);
        set_entvar(chick, var_renderamt, 36);
        server_print("%d. CHICK ENTITY %d | CT SPAWN ENTITY %d", i+1, chick, iSpawn);
    }
}

new check_tt = 0;
public check_tt_spawn(id){
    new iSpawn = ArrayGetCell(g_TTSpawn, check_tt);
    new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);
   
    set_entvar(id, var_origin, fOrigin);
    server_print("WE AT %d spawn", check_tt);

    check_tt = (check_tt + 1) % (ArraySize(g_TTSpawn));
}

new check_ct = 0;
public check_ct_spawn(id){
    new iSpawn = ArrayGetCell(g_CTSpawn, check_ct);
    new Float:fOrigin[3]; get_entvar(iSpawn, var_origin, fOrigin);
   
    set_entvar(id, var_origin, fOrigin);
    server_print("WE AT %d spawn", check_ct);

    check_ct = (check_ct + 1) % (ArraySize(g_CTSpawn));
}
#endif

public get_spawns_num(){
    if(!ArraySize(g_TTSpawn)){
        new SpawnEnt = NULLENT;
        while ((SpawnEnt = rg_find_ent_by_class(SpawnEnt, "info_player_deathmatch")) != 0){
            #if defined REPAIR
            new Float:fOrigin[3]; get_entvar(SpawnEnt, var_origin, fOrigin);
            engfunc(EngFunc_TraceHull, fOrigin, fOrigin, 0, HULL_HUMAN, 0, 0);
            if(get_tr2(0, TR_StartSolid) || get_tr2(0, TR_AllSolid) || !get_tr2(0, TR_InOpen)){
                #if defined DEBUG
                new red = rg_create_entity("info_target", false);
                set_entvar(red, var_model, MDL);
                set_entvar(red, var_modelindex, g_iModeltt);
                engfunc(EngFunc_SetOrigin, red, fOrigin);
                set_entvar(red, var_classname, "sexy_tt");
                set_entvar(red, var_movetype, MOVETYPE_NONE);
                set_entvar(red, var_solid, SOLID_NOT);
                set_entvar(red, var_renderfx, kRenderFxGlowShell);
                set_entvar(red, var_rendercolor, {255.0,0.0,0.0});
                set_entvar(red, var_rendermode, kRenderNormal);
                set_entvar(red, var_renderamt, 36);
                #endif
               
                set_entvar(SpawnEnt, var_flags, FL_KILLME);
                continue;
            }
            #endif
           
            #if defined DEBUG
            server_print("%d. TT ENTITY %d", ArraySize(g_TTSpawn), SpawnEnt);
            #endif
           
            ArrayPushCell(g_TTSpawn, SpawnEnt);
        }
    }
    if(!ArraySize(g_CTSpawn)){
        new SpawnEnt = NULLENT;
        while ((SpawnEnt = rg_find_ent_by_class(SpawnEnt, "info_player_start"))!= 0){
            #if defined REPAIR
            new Float:fOrigin[3]; get_entvar(SpawnEnt, var_origin, fOrigin);
            engfunc(EngFunc_TraceHull, fOrigin, fOrigin, 0, HULL_HUMAN, 0, 0);
            if(get_tr2(0, TR_StartSolid) || get_tr2(0, TR_AllSolid) || !get_tr2(0, TR_InOpen)){
                #if defined DEBUG
                new blue = rg_create_entity("info_target", false);
                set_entvar(blue, var_model, MDL);
                set_entvar(blue, var_modelindex, g_iModelct);
                engfunc(EngFunc_SetOrigin, blue, fOrigin);
                set_entvar(blue, var_classname, "sexy_ct");
                set_entvar(blue, var_movetype, MOVETYPE_NONE);
                set_entvar(blue, var_solid, SOLID_NOT);
                set_entvar(blue, var_renderfx, kRenderFxGlowShell);
                set_entvar(blue, var_rendercolor, {0.0,0.0,255.0});
                set_entvar(blue, var_rendermode, kRenderNormal);
                set_entvar(blue, var_renderamt, 36);
                #endif
               
                set_entvar(SpawnEnt, var_flags, FL_KILLME);
                continue;
            }
            #endif
           
            #if defined DEBUG
            server_print("%d. TT ENTITY %d", ArraySize(g_CTSpawn), SpawnEnt);
            #endif
           
            ArrayPushCell(g_CTSpawn, SpawnEnt);
        }
    }
    #if defined DEBUG
    server_print("= = = = = = = = = = =^nOLD RESULT: %d TTs AND %d CTs SPAWNS", ArraySize(g_TTSpawn), ArraySize(g_CTSpawn));
    #endif
    make_new_spawns();
}

public make_new_spawns(){
    new iTTSpawnsNum = ArraySize(g_TTSpawn);
    new iCTSpawnsNum = ArraySize(g_CTSpawn);

    new iNeedTTSpawns = max(MAX_SPAWNS - iTTSpawnsNum, 0);
    new iNeedCTSpawns = max(MAX_SPAWNS - iCTSpawnsNum, 0);
   
    #if defined DEBUG
    server_print("NEED: %d TT^n= = = = = = = = =^nSTART SEARCH TT SPAWNS^n", iNeedTTSpawns);
    #endif
    if(iNeedTTSpawns > 0){
        for(new i = 0; i < iTTSpawnsNum; i++){
            if(iNeedTTSpawns <= 0)
                break;
               
            new iSpawn = ArrayGetCell(g_TTSpawn, i);
            #if defined DEBUG
            server_print("%d. SEARCHING AROUND ENTITY %d", i+1, iSpawn);
            #endif
            new Float:fOrigin[3];
            get_entvar(iSpawn , var_origin, fOrigin);
            new Float:fNewOrigin[3];
            fNewOrigin[2] = fOrigin[2];
            for(new j = 0; j < 8; j++){
                if(iNeedTTSpawns <= 0)
                    break;
                fNewOrigin[0] = (fOrigin[0] + SEARCH_RADIUS * point[j][0]);
                fNewOrigin[1] = (fOrigin[1] + SEARCH_RADIUS * point[j][1]);
                if(is_place_ok(iSpawn, fOrigin, fNewOrigin, 48.0)){
                    new iNewSpawn = rg_create_entity("info_player_deathmatch", true);
                    if(!is_nullent(iNewSpawn)){
                        engfunc(EngFunc_SetOrigin, iNewSpawn, fNewOrigin);
                        ArrayPushCell(g_TTSpawn, iNewSpawn);
                        iNeedTTSpawns--;
                        iTTSpawnsNum++;
                    }
                }
            }
        }
    }
    #if defined DEBUG
    server_print("NEED: %d CT^n= = = = = = = = =^nSTART SEARCH CT SPAWNS^n", iNeedCTSpawns);
    #endif
    if(iNeedCTSpawns > 0){
        for(new i = 0; i < iCTSpawnsNum; i++){
            if(iNeedCTSpawns  <= 0)
                break;
            new iSpawn = ArrayGetCell(g_CTSpawn, i);
            #if defined DEBUG
            server_print("%d. SEARCHING AROUND ENTITY %d", i+1, iSpawn);
            #endif
            new Float:fOrigin[3]; get_entvar(iSpawn , var_origin, fOrigin);
            new Float:fNewOrigin[3];
            fNewOrigin[2] = fOrigin[2];
            for(new j = 0; j < 8; j++){
                if(iNeedCTSpawns <= 0)
                    break;
                fNewOrigin[0] = (fOrigin[0] + SEARCH_RADIUS * point[j][0]);
                fNewOrigin[1] = (fOrigin[1] + SEARCH_RADIUS * point[j][1]);
               
                if(is_place_ok(iSpawn, fOrigin, fNewOrigin, 48.0)){
                    new iNewSpawn = rg_create_entity("info_player_start", true);
                    if(!is_nullent(iNewSpawn)){
                        #if defined DEBUG
                        server_print("+++++++++++ADD SPAWN ENTITY %d (j %d)", iNewSpawn, j);
                        #endif
                        engfunc(EngFunc_SetOrigin, iNewSpawn, fNewOrigin);
                        ArrayPushCell(g_CTSpawn, iNewSpawn);
                        iNeedCTSpawns--;
                        iCTSpawnsNum++;
                    }
                }
            }
        }
    }
    #if defined DEBUG
    server_print("NEW RESULT: %d TTs AND %d CTs SPAWNS", ArraySize(g_TTSpawn), ArraySize(g_CTSpawn));
    #endif
    reset_spawns_num();
}

public reset_spawns_num(){
    if(ArraySize(g_TTSpawn))
        set_member_game(m_iSpawnPointCount_Terrorist, ArraySize(g_TTSpawn));
   
    if(ArraySize(g_CTSpawn))
        set_member_game(m_iSpawnPointCount_CT, ArraySize(g_CTSpawn));

    set_member_game(m_bLevelInitialized, true);
}


@CSGameRules_RestartRound_Post()
    reset_spawns_num();


@CSGameRules_GetPlayerSpawnSpot_Pre(id){
    new TeamName:team = get_member(id, m_iTeam);
    if (team != TEAM_TERRORIST && team != TEAM_CT)
        return HC_CONTINUE;

    new spot = EntSelectSpawnPoint(id, team);

    if (is_nullent(spot))
        return HC_CONTINUE;

    new Float:vecOrigin[3];    get_entvar(spot, var_origin, vecOrigin);
    new Float:vecAngles[3];    get_entvar(spot, var_angles, vecAngles);

    vecOrigin[2] += 1.0;

    set_entvar(id, var_origin, vecOrigin);
    set_entvar(id, var_v_angle, NULL_VECTOR);
    set_entvar(id, var_velocity, NULL_VECTOR);
    set_entvar(id, var_angles, vecAngles);
    set_entvar(id, var_punchangle, NULL_VECTOR);
    set_entvar(id, var_fixangle, 1);

    SetHookChainReturn(ATYPE_INTEGER, spot);
    return HC_SUPERCEDE;
}

EntSelectSpawnPoint(id, TeamName:team){
    new spotId = g_iLastSpawnId[_:team],spot,Float:vecOrigin[3];
    do{
        if (++spotId >= ArraySize(_:team == 1 ? g_TTSpawn : g_CTSpawn))
            spotId = 0;

        switch(team){
            case TEAM_TERRORIST: spot = ArrayGetCell(g_TTSpawn, spotId);
            case TEAM_CT: spot = ArrayGetCell(g_CTSpawn, spotId);
        }
        if (is_nullent(spot))
            continue;
        get_entvar(spot, var_origin, vecOrigin);
        if (!IsHullVacant(id, vecOrigin, HULL_HUMAN))
            continue;
        break;
    }
    while (spotId != g_iLastSpawnId[_:team]);
    if (is_nullent(spot))
        return 0;
    g_iLastSpawnId[_:team] = spotId;
    return spot;
}

stock bool:is_place_in_sight(iEnt, const Float:start[3], const Float:dest[3]){
    engfunc(EngFunc_TraceLine, start, dest, 0, iEnt, 0);
    new Float:fraction;    get_tr2(0, TR_flFraction, fraction);
    if(fraction == 1.0)
        return true;
    #if defined DEBUG
    server_print("------------plase isn't in signt");
    create_error_point(forigin);
    #endif
    return false;
}
//                        iSpawn        fOldOrigin        fNewOrigin        fSearchRadius
stock bool:is_place_ok(iEnt ,Float:foldorigin[3], Float:forigin[3],   Float:radius){
    // First check
    engfunc(EngFunc_TraceHull, forigin,  forigin, 0, HULL_HUMAN, 0, 0);
    if(get_tr2(0, TR_StartSolid) || get_tr2(0, TR_AllSolid) || !get_tr2(0, TR_InOpen)){

        #if defined DEBUG
        server_print("------------hull isn't vacant");
        #endif
        return false;
    }
    // Second check    if any info_player_* or func_wall or other entities..
    new Ent = NULLENT;
    while((Ent = find_ent_in_sphere(Ent, forigin, radius)) != 0){
        new szClassname[32]; get_entvar(Ent, var_classname, szClassname, 31);
        if(equal(szClassname, "func_buyzone") || equal(szClassname, "func_hostage_rescue") || equal(szClassname, "env_sprite") || equal(szClassname, "armoury_entity"))
            continue;
        #if defined DEBUG
        new classname[32]; get_entvar(Ent, var_classname, classname, charsmax(classname));
        server_print("------------%s", classname);
        #endif
        return false;
    }
    // Third check    if this point is too high over ground (f.e. tt spawns on cs_assault)
    if(distance_to_ground(forigin) > 128.0){
        #if defined DEBUG
        server_print("------------point too high");
        create_error_point(forigin);
        #endif
        return false;
    }
    // 4th check if spawn out of map
    if(engfunc(EngFunc_PointContents , forigin) == CONTENTS_SOLID){
        #if defined DEBUG
        server_print("------------CONTENTS_SOLID");
        #endif
        return false;
    }
    // 5th check if spawn out of map
    if(engfunc(EngFunc_PointContents , forigin) == CONTENTS_SKY){
        #if defined DEBUG
        server_print("------------CONTENTS_SKY");
        #endif
        return false;
    }
#if defined CHECK_VIS  
    // 6th check if start entity isn't in sight
    engfunc(EngFunc_TraceLine, foldorigin, forigin, 0, iEnt, 0);
    new Float:fraction;    get_tr2(0, TR_flFraction, fraction);
    if(fraction != 1.0){
        #if defined DEBUG
        server_print("------------plase isn't in signt");
        create_error_point(forigin);
        #endif
        return false;
    }
#endif
    return true;  
}

stock Float:distance_to_ground(Float:start[3]){
    new Float:end[3];

    end[0] = start[0];
    end[1] = start[1];
    end[2] = start[2] - 9999.0;
   
    new ptr = create_tr2();
    engfunc(EngFunc_TraceHull, start, end, IGNORE_MONSTERS, HULL_HUMAN, 0, ptr);
    new Float:distance;
    get_tr2(ptr, TR_flFraction, distance);
    free_tr2(ptr);
    distance *= 9999.0;
    return distance;
}

bool:IsHullVacant(id, Float:vecOrigin[3], hull){
    engfunc(EngFunc_TraceHull, vecOrigin, vecOrigin, 0, hull, id, 0);
    if (get_tr2(0, TR_StartSolid) || get_tr2(0, TR_AllSolid) || !get_tr2(0, TR_InOpen))
        return false;
    return true;
}

#if defined DEBUG

stock create_error_point(Float:fOrigin[3]){
    new flag = rg_create_entity("info_player_deathmatch", false);
    set_entvar(flag, var_model, FLAG_MDL);
    set_entvar(flag, var_modelindex, g_iModel2);
    engfunc(EngFunc_SetOrigin, flag, fOrigin);
    set_entvar(flag, var_classname, "sexy_flag");
    set_entvar(flag, var_movetype, MOVETYPE_NONE);
    set_entvar(flag, var_solid, SOLID_NOT);
}
#endif
1636058557202.png1636058872127.png

PS ни одна курочка не пострадала.
PS2 команды на удаление тестовых курочек пока нет, надо сервер перезагружать.

PS3 сейчас все спавны создаёт в одной плоскости, дальше сделаю проверку по высоте, что бы можно было спавны делать даже если рядом наклонная плоскость/яма/ящик. на скрине в том месте, где не выполнена проверка по высоте стоит флажок.
1636060836897.png
 
Последнее редактирование:
Сообщения
486
Реакции
89
Помог
5 раз(а)
iPlague, на данный момент - это крайняя версия? Или будут еще обновления?
 
Сообщения
1,570
Реакции
652
Помог
5 раз(а)
Для чего если есть resemiclip + принудительное изменение количества спавнов террористов и кт через set_member

2 строки кода и никаких проблем со спавнами даже на half life картах можно играть
 
Сообщения
1,570
Реакции
652
Помог
5 раз(а)
WILL_BE, Unreal Spawn Fixer там проверяет есть ли ReSemiclip/TeamSemiclip если нет то включается встроенный Semiclip, и включает соответствующие квары в ReGameDLL для работы.

 
Последнее редактирование:

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу