Найти игроков на нужной дистанции

NeuraL

Участник
Сообщения
13
Реакции
1
Всем привет. Мне нужна функция, чтобы найти игроков на определенном расстоянии. Но так, чтобы она не игнорировала стены и пропы, то есть если между игроками есть что-то, то тогда нужно исключить данного игрока из поиска.

Я использовал эту функцию из плагина для турели, которая находит ближайшего игрока:
C-подобный:
stock FindFirstTargetInRange(client, Float:vAngle[3], Float:vEntPosition[3], Float:entHeight = 60.0)
{
    new Float:vClientPosition[3];
  
    GetEntPropVector(client, Prop_Send, "m_vecOrigin", vEntPosition);
    vEntPosition[2] += entHeight;
  
    new Float:fDistance[MAXPLAYERS + 1];
    new iTager[MAXPLAYERS + 1];
    new numb = 0;
    for (new i = 1; i <= MaxClients; i++)
    {
  if (IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(client) != GetClientTeam(i))
  {
   GetClientEyePosition(i, vClientPosition);
   fDistance[numb] = GetVectorDistance(vClientPosition, vEntPosition, false);
   if(fDistance[numb] <= 9000000 && fDistance[numb] > 50)
   {
    iTager[numb] = i;
    numb++;  
   }
  }
    }
  
    new zombie = GetZombieMinDistance(iTager, fDistance, numb+1);
    if(zombie < 1 || !IsClientInGame(zombie) || !IsPlayerAlive(zombie))
    {
  return (-1);
    }
  
    GetClientEyePosition(zombie, vClientPosition);
    vClientPosition[2] -= 10.0;
    MakeVectorFromPoints(vEntPosition, vClientPosition, vAngle);
    vClientPosition[2] += 10.0;
    //NormalizeVector(vAngle, vAngle);
    GetVectorAngles(vAngle, vAngle);
  
    new Handle:trace = TR_TraceRayFilterEx(vEntPosition, vClientPosition, MASK_SHOT, RayType_EndPoint, TraceASDF, client);
    if(!TR_DidHit(trace))
    {
  CloseHandle(trace);
  return (zombie);
    }
    CloseHandle(trace);
    return (-1);
}

GetZombieMinDistance(clients[], Float:distance[], max_clients)
{
    if(max_clients < 1)
    {
  return -1;
    }
    new Float:min_distance = 999999999.0;
    new zombie = -1;
    for(new i = 0; i < max_clients; i++)
    {
  // 999999999   >  10000000
  if(min_distance > distance[I])
  {
   if(distance[I] <= 9000000009 && distance[I] > 50)
   {
    min_distance = distance[I];
    zombie = clients[I];   
   }
  }
    }
    return zombie;
}

public bool:TraceASDF(entity, mask, any:data)
{
    return (data != entity);
}

Но она очень плохо работает, зачастую не видит игрока в упор, даже если между ними ничего нет. Не подскажите, как можно сделать нормальную функцию? Заранее спаибо
prikol
 
Последнее редактирование:
Решение
Но это же все равно нанесет урон через стены?
Да.
Для этой энтити можно задать радиус действия через DispatchKeyValueFloat(point_hurt, "DamageRadius", 5.0); (ширина/глубина прямоугольного параллелепипеда которого занимает игрок равна 33 юнитам).
Или же фильтровать цели через targetname, если нужно накрытие большой области
я что-то не правильно в таргетнэйм вписал? Я просто вписал айди клиента
Да, неправильно: тот таргетнейм, что ты прописываешь для point_hurt, должен присваиваться и цели:
C-подобный:
    static char buffer[128];
    Format(buffer, sizeof(buffer), "PointHurtAtTarget:%d", target);    // любая хрень, главное, чтобы уникальная
    DispatchKeyValue(target, "TargetName", buffer);    // прописываем этот текст...

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
Начни хотя бы стого, что используй для кода тег [CODE=clike]твой_код[/CODE] вместо тега спойлера.
 

NeuraL

Участник
Сообщения
13
Реакции
1
Начни хотя бы стого, что используй для кода тег [CODE=clike]твой_код[/CODE] вместо тега спойлера.
Исправил. Данный плагин сделан для зомби мода, чтобы турель стреляла в первого попавшегося по близости зомби. Но поскольку там таймер постоянно работает, то турели не нужен точный код, зомби немного сдвинулся и турель его заметила, а в моем случае так не получится, т.к. проверку я делаю только один раз. Вот не знаю, как обнаружить стену и пропы между игроками
 

Reiko1231

AlexTheRegent
Сообщения
508
Реакции
1,335
Точную функцию тут не написать по ряду причин: сложная геометрия моделей, большой объём вычислений. Поэтому для решения этой задачи идут на различные упрощения. Я могу предложить следующий вариант улучшения кода:
Увеличить число точек для трассировки в несколько раз. Сейчас проверяется, видит ли оружие точку глаз игрока, а можно так же подобрать точки для рук, ног, туловища, причём по несколько на каждую часть тела. Чем больше будет разных точек, тем точнее функция будет вычислять, видно игрока или нет. Но при этом сначала нужно составить массив игроков, которых нужно проверять, а затем проверять их в нужном порядке (например от самого близкого игрока к самому дальнему) до первого видимого игрока.
 

NeuraL

Участник
Сообщения
13
Реакции
1
Точную функцию тут не написать по ряду причин: сложная геометрия моделей, большой объём вычислений. Поэтому для решения этой задачи идут на различные упрощения. Я могу предложить следующий вариант улучшения кода:
Увеличить число точек для трассировки в несколько раз. Сейчас проверяется, видит ли оружие точку глаз игрока, а можно так же подобрать точки для рук, ног, туловища, причём по несколько на каждую часть тела. Чем больше будет разных точек, тем точнее функция будет вычислять, видно игрока или нет. Но при этом сначала нужно составить массив игроков, которых нужно проверять, а затем проверять их в нужном порядке (например от самого близкого игрока к самому дальнему) до первого видимого игрока.
Я вот так менял глаза конкретного игрока от -300 до 400:
C-подобный:
new players[MAXPLAYERS+1]
for (new i = 1; i <= MaxClients; i++)
    {
        players[i] = -1;
    }
    vEntPosition[2] = -300.0;
    new Handle:trace;
    for (new igroks = 1; igroks <= MaxClients; igroks++)
    {
        if (!IsClientConnected(igroks) || !IsClientInGame(igroks) || !IsPlayerAlive(igroks) || players[igroks] == 1)
        {
            continue;
        }
        for (new j = 1; j <= 700; j++)
        {
            vEntPosition[2] += 1;
            GetClientEyePosition(igroks, vClientPosition);
            vClientPosition[2] -= 10.0;
            MakeVectorFromPoints(vEntPosition, vClientPosition, vAngle);
            vClientPosition[2] += 10.0;
            //NormalizeVector(vAngle, vAngle);
            GetVectorAngles(vAngle, vAngle);
            trace = TR_TraceRayFilterEx(vEntPosition, vClientPosition, MASK_SHOT, RayType_EndPoint, TraceASDF, client);
            if(!TR_DidHit(trace))
            {
                players[igroks] = 1;
                CloseHandle(trace);
                continue;
            }
            CloseHandle(trace);
        }
    }
Но он всё равно плохо обнаруживает, может менять с шагом меньше единицы? Или ещё также менять vEntPosition[0] и vEntPosition[1]?
 

NeuraL

Участник
Сообщения
13
Реакции
1
А между тем высота модельки игрока порядка 70 юнитов:
В том и дело. что даже так он не обнаруживает, но вероятно мне стоило менять не только высоту но и другие координаты, но я боюсь, что будет чрезмерная нагрузка
 

NeuraL

Участник
Сообщения
13
Реакции
1
Точную функцию тут не написать по ряду причин: сложная геометрия моделей, большой объём вычислений. Поэтому для решения этой задачи идут на различные упрощения. Я могу предложить следующий вариант улучшения кода:
Увеличить число точек для трассировки в несколько раз. Сейчас проверяется, видит ли оружие точку глаз игрока, а можно так же подобрать точки для рук, ног, туловища, причём по несколько на каждую часть тела. Чем больше будет разных точек, тем точнее функция будет вычислять, видно игрока или нет. Но при этом сначала нужно составить массив игроков, которых нужно проверять, а затем проверять их в нужном порядке (например от самого близкого игрока к самому дальнему) до первого видимого игрока.
Есть какие-нибудь уже готовые функции, чтобы нанести урон игрокам в определенном месте? Например я даю координаты, и все кто будут на определенной дистанции от них, получат урон, но только если они не за текстурами, например также, как с осколочной гранатой. Может можно заспавнить осколочную, но увеличить её радиус действия и сделать беззвучной?
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
Создаёшь point_hurt (и прописываешь ей targetname если нужно чтобы повреждение наносилось не всем игрокам в радиусе действия)
C-подобный:
stock bool Entity_Hurt(int target, int damage, int attacker=0, int damageType=DMG_GENERIC, const float pos[3])
{
    static int point_hurt;
    if((point_hurt = CreateEntityByName("point_hurt")) == -1)
        return false;

    static char buffer[128];
    Format(buffer, sizeof(buffer), "PointHurtAtTarget:%d", target);
    DispatchKeyValue(target, "TargetName", buffer);
    DispatchKeyValue(point_hurt, "DamageTarget", buffer);
    DispatchKeyValueVector(point_hurt, "Origin", pos);

    IntToString(damage, buffer, sizeof(buffer));
    DispatchKeyValue(point_hurt,"Damage", buffer);

    IntToString(damageType, buffer, sizeof(buffer));
    DispatchKeyValue(point_hurt,"DamageType", buffer);

    DispatchKeyValueFloat(point_hurt, "DamageRadius", 5.0);

    DispatchKeyValue(point_hurt, "classname", WPN_TYPE[iWeapon+1]);

    DispatchSpawn(point_hurt);

    AcceptEntityInput(point_hurt, "Hurt", attacker);
    AcceptEntityInput(point_hurt, "Kill");

    return true;
}
Сообщения автоматически склеены:

ну или так: если нужно наносить урон оружием, которое находится у игрока в руках: SDKHooks_TakeDamage(victim, attaker, attaker, fDamage, damageType, _, NULL_VECTOR, EnemyPos);
 

Reiko1231

AlexTheRegent
Сообщения
508
Реакции
1,335
не только высоту но и другие координаты, но я боюсь, что будет чрезмерная нагрузка
Нужно не только высоту. Нет, чрезмерная нагрузка в текущем варианте с 700 трассировками на одного игрока. Я предлагаю сделать по 2 на часть тела (на голову можно и один), итого в районе 6 трассировок на игрока (2 ноги, 2 руки, туловище, голова). Но нужно учесть, что положение рук и ног меняется при ходьбе\прицеливании.
Кроме этого, у вас ошибка - координаты глаз находятся в переменной vClientPosition, вы же варьируете vEntPosition.
 

NeuraL

Участник
Сообщения
13
Реакции
1
Создаёшь point_hurt (и прописываешь ей targetname если нужно чтобы повреждение наносилось не всем игрокам в радиусе действия)
Но это же все равно нанесет урон через стены? Или я что-то не правильно в таргетнэйм вписал? Я просто вписал айди клиента, которого мне нужно ранить, ещё закомментировал эту строчку:
C-подобный:
//DispatchKeyValue(point_hurt, "classname", WPN_TYPE[iWeapon+1]);
Но урон всё равно проходит и через стены и через пропы
Сообщения автоматически склеены:

Нужно не только высоту. Нет, чрезмерная нагрузка в текущем варианте с 700 трассировками на одного игрока. Я предлагаю сделать по 2 на часть тела (на голову можно и один), итого в районе 6 трассировок на игрока (2 ноги, 2 руки, туловище, голова). Но нужно учесть, что положение рук и ног меняется при ходьбе\прицеливании.
Кроме этого, у вас ошибка - координаты глаз находятся в переменной vClientPosition, вы же варьируете vEntPosition.
Точно, я совсем не заметил, что не то изменял..

Я сделал функцию, которая должна заполнять массив конкретного игрока, всеми другими игроками, которых он видит не через пропы и текстуры:
C-подобный:
FindAllTargets(client, players[])
{
    new Float:vClientPosition[3]; // позиция глаз клиента
    new Float:vAngle[3], // угол, я не понимаю, для чего он
    Float:vEntPosition[3]; // куда я записываю позицию игроков
    GetClientEyePosition(client, vClientPosition);
    for (new i = 1; i <= MaxClients; i++) // изначально клиент не видит никого
    {
        players[i] = -1;
    }
    new Handle:trace;
    for (new igroks = 1; igroks <= MaxClients; igroks++) // порядка 4600 проверок, слишком много
    {
        if (!IsClientConnected(igroks) || !IsClientInGame(igroks) || !IsPlayerAlive(igroks) || players[igroks] == 1)
        {
            continue;
        }
        GetClientEyePosition(client, vClientPosition);
        vClientPosition[0] -= 60;
        vClientPosition[1] -= 60;
        vClientPosition[2] -= 30;
        for (new t = 1; t <= 5; t++)
        {
            vClientPosition[0] += 20;
            if (!IsClientConnected(igroks) || !IsClientInGame(igroks) || !IsPlayerAlive(igroks) || players[igroks] == 1)
            {
                continue;
            }
            for (new k = 1; k <= 5; k++)
            {
                vClientPosition[1] += 20;
                if (!IsClientConnected(igroks) || !IsClientInGame(igroks) || !IsPlayerAlive(igroks) || players[igroks] == 1)
                {
                    continue;
                }
                for (new j = 1; j <= 6; j++)
                {
                    vClientPosition[2] += 10;
                    GetEntPropVector(igroks, Prop_Send, "m_vecOrigin", vEntPosition);
                    vClientPosition[2] -= 10.0;
                    MakeVectorFromPoints(vEntPosition, vClientPosition, vAngle);
                    vClientPosition[2] += 10.0;
                    //NormalizeVector(vAngle, vAngle); не знаю, за что отвечает, пробовал и убирать и оставлять, разницы нет
                    GetVectorAngles(vAngle, vAngle);
                    trace = TR_TraceRayFilterEx(vEntPosition, vClientPosition, MASK_SHOT, RayType_EndPoint, TraceASDF, client);
                    if(!TR_DidHit(trace))
                    {
                        players[igroks] = 1;
                        CloseHandle(trace);
                        continue;
                    }
                    CloseHandle(trace);
                }
            }
        }
    }
}

Но с таким кодом, теперь он и обнаруживает тех, кто за стеной.
Как можно улучшить его? Уменьшить шаг и сделать меньше проверок? На сколько юнитов мне стоит отклонять нулевую и первую координату?
 
Последнее редактирование:

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
Но это же все равно нанесет урон через стены?
Да.
Для этой энтити можно задать радиус действия через DispatchKeyValueFloat(point_hurt, "DamageRadius", 5.0); (ширина/глубина прямоугольного параллелепипеда которого занимает игрок равна 33 юнитам).
Или же фильтровать цели через targetname, если нужно накрытие большой области
я что-то не правильно в таргетнэйм вписал? Я просто вписал айди клиента
Да, неправильно: тот таргетнейм, что ты прописываешь для point_hurt, должен присваиваться и цели:
C-подобный:
    static char buffer[128];
    Format(buffer, sizeof(buffer), "PointHurtAtTarget:%d", target);    // любая хрень, главное, чтобы уникальная
    DispatchKeyValue(target, "TargetName", buffer);    // прописываем этот текст цели
    DispatchKeyValue(point_hurt, "DamageTarget", buffer); // прописываем инфу о цели
ещё закомментировал эту строчку
Ты убрал значение параметра, который указывает из какого оружия наносится урон
Сообщения автоматически склеены:

Я сделал функцию
OMG, что за говнокод?
Сделайте мне развидеть это.
Сообщения автоматически склеены:

Вот так можно сделать проверку может ли видеть ли один игрок голову другого:
C-подобный:
static const float HULL_SIZES[][]    = {{-2.0, -2.0, -2.0}, {2.0, 2.0, 2.0}}; // 4x4x4

stock int GetAvailableTargets(int client, int targets[], int maxsize)
{
    int i, t, num;
    static float pos[3], eye[3];
    GetClientEyePosition(client, eye);
    for(i = 1, t = GetClientTeam(client); i <= MaxClients && num < maxsize; ++i)
        if(i != client && IsClientInGame(i) && IsPlayerAlive(i) && t != GetClientTeam(i))
        {
            GetClientEyePosition(i, pos);
            // проверяем есть ли препятствия м/у глазами игроков
            TR_TraceHullFilter(eye, pos, HULL_SIZES[0], HULL_SIZES[1], CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_DEBRIS|CONTENTS_HITBOX, DontHitOwnerOrNade, client);
            // если препятствий нет, то заносим в массив
            if(TR_GetEntityIndex() == i) targets[num++] = i;
        }
    return num;
}
 
Последнее редактирование:
Решение

NeuraL

Участник
Сообщения
13
Реакции
1
Вот так можно сделать проверку может ли видеть ли один игрок голову другого:
C-подобный:
static const float HULL_SIZES[][] = {{-2.0, -2.0, -2.0}, {2.0, 2.0, 2.0}}; // 4x4x4

stock int GetAvailableTargets(int client, int targets[], int maxsize)
{
int i, t, num;
static float pos[3], eye[3];
GetClientEyePosition(client, eye);
for(i = 1, t = GetClientTeam(client); i <= MaxClients && num < maxsize; ++i)
if(i != client && IsClientInGame(i) && IsPlayerAlive(i) && t != GetClientTeam(i))
{
GetClientEyePosition(i, pos);
// проверяем есть ли препятствия м/у глазами игроков
TR_TraceHullFilter(eye, pos, HULL_SIZES[0], HULL_SIZES[1], CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_DEBRIS|CONTENTS_HITBOX, DontHitOwnerOrNade, client);
// если препятствий нет, то заносим в массив
if(TR_GetEntityIndex() == i) targets[num++] = i;
}
return num;
}

Но это проверит только голову, например, если он видит только ноги или только туловище игрока, то тогда он не попадет в массив, может тогда использовать не координаты глаз противника, а его местоположение?
Если пытаться улучшить этот код, как предложил Reiko1231, то нужно проверять видят ли глаза, руки и тело клиента, глаза руки или тело противника? Значит нужно сделать еще 4 трассировки, с координатами рук, ног и туловища, и ещё на каждую по 5 трассировок, с координатами глаз, рук, ног и туловища противника?
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
да
только вместо координат глаз целей нужно получать координаты нужных частей тела/конечностей
 

NeuraL

Участник
Сообщения
13
Реакции
1
да
только вместо координат глаз целей нужно получать координаты нужных частей тела/конечностей
Большое спасибо, надеюсь справлюсь. За говнокод простите, как умею 😁
Сообщения автоматически склеены:

Нужно не только высоту. Нет, чрезмерная нагрузка в текущем варианте с 700 трассировками на одного игрока. Я предлагаю сделать по 2 на часть тела (на голову можно и один), итого в районе 6 трассировок на игрока (2 ноги, 2 руки, туловище, голова). Но нужно учесть, что положение рук и ног меняется при ходьбе\прицеливании.
Кроме этого, у вас ошибка - координаты глаз находятся в переменной vClientPosition, вы же варьируете vEntPosition.
Большое спасибо за помощь!
 

Reiko1231

AlexTheRegent
Сообщения
508
Реакции
1,335
Для взрыва лучше использовать сущность env_explosion, он уже должен будет как граната работать (можно задать свой радиус, урон, выключить модель взрыва и поставить свою, и тд, плюс не должен пробивать через стены). Вот тут вроде есть пример создания [CS:GO] Adding knock back on env_explosion - AlliedModders . Второй вариант это использовать трассировки, чтобы определить, кому нужно наносить урон, а кому нет (так же проверять точки, видно ли их из точки взрыва или нет).
@NeuraL, учитывая, как вы написали функцию перебора точек тела до этого, вы не справитесь с руками\ногами, т.к. они находятся в относительном положении от головы, а в мире вы перебирали точки в абсолютных координатах. Код просто не будет учитывать повороты игрока. Если хотите сделать это нормально, напишите мне в дискорд, а то тут крайне неудобно будет прикладывать картинки, чтобы объяснить, в чём проблема и как учесть это. А потом уже выложите здесь код, который получится, чтобы кто-нибудь другой смог потом его найти.
Ну и в дополнение хочу сказать что трассировка hull'ами довольно ненадежная, т.к. достаточно краем закрыть голову (буквально одним пикселем) и код уже скажет, что игрока не видно. Трассировка лучами по опорным точкам работает точнее.
 

NeuraL

Участник
Сообщения
13
Реакции
1
Для взрыва лучше использовать сущность env_explosion, он уже должен будет как граната работать (можно задать свой радиус, урон, выключить модель взрыва и поставить свою, и тд, плюс не должен пробивать через стены). Вот тут вроде есть пример создания [CS:GO] Adding knock back on env_explosion - AlliedModders .
То есть как вариант определения видимости игрока, можно спавнить хаешку от него, убрать звук и анимацию, сделать максимальный радиус, а нанесенный урон восстановить всем раненным в захуконном событии player_hurt_pre. И так даже никто не заметит ничего и с помощью этого получиться лучше всего определять игроков в зоне видимости?
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,520
Реакции
4,979
можно спавнить хаешку от него, убрать звук и анимацию, сделать максимальный радиус, а нанесенный урон восстановить всем раненным в захуконном событии player_hurt_pre
Можно, но не нужно: мы решаем проблемы, которые сами себе и создаём.
 

tonline_kms65_1

Участник
Сообщения
565
Реакции
225
Если вот так пытаться решить задачу, можно будет ооой как долго решать.
А если вот так ткнуть, а если так.
Попытайся понять сам принцип работы хотя бы того кода, который где то оттопырил. Просто сядь, подумай. Там же все понятно, просто нужно подумать, тебе подумать а не нам помогать тебе с чужим кодом.
Что за беда то такая, ведь были же люди как люди....
Сообщения автоматически склеены:

да
только вместо координат глаз целей нужно получать координаты нужных частей тела/конечностей
А как ты собираешся получить эти самые координаты, я стесняюсь спросить?
Как по мне - получаешь размеры игрока(коробка) макс. мин. , эти значения передаешь в трассировку.
Сомневаюсь, что можно отдельно отследить хитбоксы модели(они одной группой). Хотя, возможно, я ошибаюсь.
 
Последнее редактирование:
Сверху Снизу