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

iPlague

♿️
Сообщения
188
Реакции
71
Помог
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

♿️
Сообщения
188
Реакции
71
Помог
2 раз(а)
Pokemoshka, спасибо! знач возьму полезное и хорошее откуда и буду дальше делать.
 
Сообщения
483
Реакции
322
Помог
1 раз(а)
iPlague,
+ но,как по мне - лучше это всё делать вручную.
Да,ты потратишь много времени на все свои карты,если их будет порядка свыше 30 штук,но зато - это будет расстановка в нужных и правильных тебе местах
 
Сообщения
261
Реакции
89
Помог
7 раз(а)
Тут получается расставляется спавны не рандомно а только близко к своей спавны?


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

iPlague

♿️
Сообщения
188
Реакции
71
Помог
2 раз(а)
Limbooc, расстановка идёт вокруг уже существующих спавнов, не рандомно а по схеме.
13 Окт 2021
Предложение: генерировать создание спавн не зависимости от команды
это как? рандомно по карте? если да - то работаю над алгоритмом. не знаю, получится ли, но попробовать хочу.
 
Сообщения
256
Реакции
97
Помог
3 раз(а)
iPlague, ну тут тоже бы как-то в углах, на ящиках или тому подобное
 
Сообщения
261
Реакции
89
Помог
7 раз(а)
Limbooc, расстановка идёт вокруг уже существующих спавнов, не рандомно а по схеме.
13 Окт 2021

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

iPlague

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

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

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