Русское сообщество по скриптингу

Проверка виден ли игрок

Статьи или фрагменты кода для новичков и уже опытных скриптеров по AMXX.

Модератор: Chuvi

Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по AMX Mod X.

Проверка виден ли игрок

Сообщение DJ_WEST » 16 сен 2009, 13:20

Автор: ot_207
Перевод: DJ_WEST

Данная статья больше всего посвящена скриптерам, которые умеют работать с векторами. Но я постараюсь описать все более подробно, чтобы данную статью смог понять каждый человек. Это очень эффективный метод, не считая того, что есть ситуации, когда вы не видите игрока. Но это не будет проблемой. Вы можете использовать данный метод для своего мода, все что вам требуется немного изменить код.

1. Константы
Эти константы используются, как константы умножения.Они применяются при создании некоторых массивов, которые используются для вычисления, если игрок будет видимым.
Код: Выделить всё
#define GENERAL_X_Y_SORROUNDING             18.5         // 16.0 
#define CONSTANT_Z_CROUCH_UP                 31.25         // 32.0 
#define CONSTANT_Z_CROUCH_DOWN                 17.5         // 16.0 
#define CONSTANT_Z_STANDUP_UP                 34.0         // 36.0 
#define CONSTANT_Z_STANDUP_DOWN             35.25         // 36.0 

#define GENERAL_X_Y_SORROUNDING_HALF        9.25         // 8.0 
#define GENERAL_X_Y_SORROUNDING_HALF2        12.0         // 8.0 
#define CONSTANT_Z_CROUCH_UP_HALF             15.5         // 16.0 
#define CONSTANT_Z_CROUCH_DOWN_HALF            8.75         // 8.0 
#define CONSTANT_Z_STANDUP_UP_HALF            17.0         // 18.0 
#define CONSTANT_Z_STANDUP_DOWN_HALF        17.5         // 18.0 

#define ANGLE_COS_HEIGHT_CHECK                0.7071        // cos(45  градусов)  


2. Массивы
Теперь нам нужны некоторые массивы для вычислений.
Код: Выделить всё
// Используется для определения головной точки оружия
new const Float:weapon_edge_point[CSW_P90+1] = 
{ 
    0.00
, 35.5, 0.00, 42.0, 0.00, 35.5, 0.00, 37.0, 37.0, 0.00, 35.5, 35.5, 32.0, 41.0, 32.0, 36.0, 41.0, 35.5, 41.0, 32.0, 37.0, 35.5, 42.0, 41.0, 44.0, 0.00, 35.5, 37.0, 32.0, 0.00, 32.0 
} 

// Используется в качестве массива для умножения
new const Float:vec_multi_lateral[] =  
{ 
    GENERAL_X_Y_SORROUNDING
, 
    
-GENERAL_X_Y_SORROUNDING, 
    GENERAL_X_Y_SORROUNDING_HALF2
, 
    
-GENERAL_X_Y_SORROUNDING_HALF 
} 

// Используется в качестве высоты, если игрок приседает
new const Float:vec_add_height_crouch[] = 
{ 
    CONSTANT_Z_CROUCH_UP
, 
    
-CONSTANT_Z_CROUCH_DOWN, 
    CONSTANT_Z_CROUCH_UP_HALF
, 
    
-CONSTANT_Z_CROUCH_DOWN_HALF 
} 

// Используется в качестве высоты, если игрок встает
new const Float:vec_add_height_standup[] = 
{ 
    CONSTANT_Z_STANDUP_UP
, 
    
-CONSTANT_Z_STANDUP_DOWN, 
    CONSTANT_Z_STANDUP_UP_HALF
, 
    
-CONSTANT_Z_STANDUP_DOWN_HALF 
}
  


3. Необходимые переменные

Нам нужно прослеживать путь (точки видимости) для сохранения результатов:
Код: Выделить всё

new thld 

public plugin_init
() 
{ 
    
// Мы создаем его в plugin_init
    thdl = create_tr2() 
} 

public plugin_end
() 
{ 
    
// Мы удаляем его в plugin_end
    free_tr2(thdl) 
}  

Нам также необходим список для хранения типов объектов, если наш объект прозрачен или нет. Это необходимо при частой проверки видимости игрока.
Код: Выделить всё
// Данная бит-сумма позволяет хранить до 2048 объектов.
new bs_array_transp[64]                // Бит-сумма, которая равняется 64*32
new bs_array_solid[64]                 // Бит-сумма, которая равняется 64*32
 


Для удобства работы с данными бит-суммами были сделаны следующие макросы:
Код: Выделить всё
#define add_transparent_ent(%1)         bs_array_transp[((%1 - 1) / 32)] |= (1<<((%1 - 1) % 32)) 
#define del_transparent_ent(%1)         bs_array_transp[((%1 - 1) / 32)] &= ~(1<<((%1 - 1) % 32)) 
#define  is_transparent_ent(%1)        (bs_array_transp[((%1 - 1) / 32)] & (1<<((%1 - 1) % 32))) 
#define add_solid_ent(%1)               bs_array_solid[((%1 - 1) / 32)] |= (1<<((%1 - 1) % 32)) 
#define del_solid_ent(%1)               bs_array_solid[((%1 - 1) / 32)] &= ~(1<<((%1 - 1) % 32)) 
#define  is_solid_ent(%1)              (bs_array_solid[((%1 - 1) / 32)] & (1<<((%1 - 1) % 32))) 
 


4. Необходимые функции
Трассировочные функции:
Код: Выделить всё
// Проверяет является ли точка видимой от начало до нее. Игнорируются стекло, игроки и ignore_ent. 
bool:is_point_visible(const Float:start[3], const Float:point[3], ignore_ent) 
{ 
    engfunc
(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl) 

    static Float
:fraction 
    get_tr2
(thdl, TR_flFraction, fraction) 
     
    
// Возвращение результата
    // Если 1, значит мы ничего не коснулись 
    return (fraction == 1.0) 
} 

// Тоже самое, что и верхняя функция, только проверяем объект, который мы коснулись и через который можно видеть
bool:is_point_visible_texture(const Float:start[3], const Float:point[3], ignore_ent) 
{ 
    engfunc
(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl) 
     
    
// Сохраняем объект
    static ent 
    ent 
= get_tr2(thdl, TR_pHit) 

    static Float
:fraction 
    get_tr2
(thdl, TR_flFraction, fraction) 
     
    
// Если мы ударили что-то, то начать проверку
    if (fraction != 1.0 && ent > 0) 
    
{ 
        
// Это означает, что мы не знаем, чего мы коснулись
        if (!is_transparent_ent(ent) && !is_solid_ent(ent)) 
        
{ 
            static texture_name
[2] 
            static Float
:vec[3] 
            
// Эти вычисления сделаны в целях безопасности
            // функция TraceTexture будет убивать сервер на маленький дистанциях
            xs_vec_sub(point, start, vec) 
            xs_vec_mul_scalar
(vec, (5000.0 / xs_vec_len(vec)), vec) 
            xs_vec_add
(start, vec, vec) 
             
            engfunc
(EngFunc_TraceTexture, ent, start, vec, texture_name, charsmax(texture_name)) 
             
            
// Если texture_name начинается с { - это означает, что у нас прозрачная текстура,  
            // если да, то мы делаем перетрассировку и добавляем данный объект, как прозрачный.  
            // Если нет, то мы добавляем объект, как непрозрачный и следовательно нам не нужно его снова проверять.
            if (equal(texture_name, "{")) 
            
{ 
                add_transparent_ent
(ent) 
                ignore_ent 
= ent 
                engfunc
(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl) 
                get_tr2
(thdl, TR_flFraction, fraction) 
                return 
(fraction == 1.0) 
            
} 
            else 
            
{ 
                add_solid_ent
(ent) 
                return 
(fraction == 1.0) 
            
} 
        
} 
        
// Это означает, что объект или прозрачный, или нет
        else 
        
{ 
            if 
(is_solid_ent(ent)) 
            
{ 
                return 
(fraction == 1.0) 
            
} 
            else 
            
{ 
                ignore_ent 
= ent 
                engfunc
(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl) 
                get_tr2
(thdl, TR_flFraction, fraction) 
                return 
(fraction == 1.0) 
            
} 
        
} 
    
} 
     
    return 
(fraction == 1.0) 
} 
 


Векторные функции [взято из xs.inc]:
Код: Выделить всё
// Сложение двух векторов
stock xs_vec_add(const Float:in1[], const Float:in2[], Float:out[]) 
{ 
    out
[0] = in1[0] + in2[0]; 
    out
[1] = in1[1] + in2[1]; 
    out
[2] = in1[2] + in2[2]; 
} 

// Вычитание двух векторов
stock xs_vec_sub(const Float:in1[], const Float:in2[], Float:out[]) 
{ 
    out
[0] = in1[0] - in2[0]; 
    out
[1] = in1[1] - in2[1]; 
    out
[2] = in1[2] - in2[2]; 
} 

// Умножение вектора на скалярную величину
stock xs_vec_mul_scalar(const Float:vec[], Float:scalar, Float:out[]) 
{ 
    out
[0] = vec[0] * scalar; 
    out
[1] = vec[1] * scalar; 
    out
[2] = vec[2] * scalar; 
} 

// Возвращает длину вектора
stock Float:xs_vec_len(const Float:vec[3]) 
{ 
    return floatsqroot
(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); 
} 

// Умножение двух векторов
stock Float:xs_vec_dot(const Float:vec[], const Float:vec2[]) 
{ 
    return 
(vec[0]*vec2[0] + vec[1]*vec2[1] + vec[2]*vec2[2]) 
} 

// Проверка векторов на равность
bool:xs_vec_equal(const Float:vec1[], const Float:vec2[]) 
{ 
    return 
(vec1[0] == vec2[0]) && (vec1[1] == vec2[1]) && (vec1[2] == vec2[2]); 
} 
 


5. Известные прозрачные объекты (через которые можно видеть)
Код: Выделить всё

public plugin_precache
() 
{ 
    register_forward
(FM_Spawn, "fw_spawn", 1) 
} 

public fw_spawn
(ent) 
{ 
    if 
(!pev_valid(ent)) 
        return FMRES_IGNORED 
     
    static rendermode
, Float:renderamt 
     
    rendermode 
= pev(ent, pev_rendermode) 
    pev
(ent, pev_renderamt, renderamt) 
     
    
// Если объект прозрачный, добавить в список
    if (((rendermode == kRenderTransColor || rendermode == kRenderGlow || rendermode == kRenderTransTexture) && renderamt < 255.0) || (rendermode == kRenderTransAdd)) 
    
{ 
        add_transparent_ent
(ent) 
        return FMRES_IGNORED 
    
} 
     
    return FMRES_IGNORED 
} 
 


6. Создание функции видимости игрока
Теперь имея константы, массивы, переменные и функции, мы можем создать функцию, которая будет определять виден ли нам игрок.

Код: Выделить всё

stock is_player_visible(visor, target)
{
    // Объявления
    static Float:origin[3], Float:start[3], Float:end[3], Float:addict[3], Float:plane_vec[3], Float:normal[3], ignore_ent
     
    // Игнорирумый объект - visor. В данном случае используем его в качестве теста.
    ignore_ent = visor
     
    // Проверяем находится ли игрок позади объекта visor
    // Получаем координаты visor
    pev(visor, pev_origin, origin)
     
    // Получаем угол наклона visor
    pev(visor, pev_v_angle, normal)

    // Конвертируем вектор
    angle_vector(normal, ANGLEVECTOR_FORWARD, normal)
     
    // Получаем коодиранты цели
    pev(target, pev_origin, end)

    // Делаем вычитание векторов дял получения одного вектора
    xs_vec_sub(end, origin, plane_vec)

    // Получаем реальные данные между двумя векторами
    xs_vec_mul_scalar(plane_vec,  (1.0/xs_vec_len(plane_vec)), plane_vec)
     
    // После умножение двух векторов их длина равняется 1
    // Вычисляем косинус угла между ними, если он будет отрицательным - значит игрок находится позади, возвращаем false
    if (xs_vec_dot(plane_vec, normal) < 0)
    {
        return false
    }
     
    // Получаем смещение обзора visor и добавляем их к координатам для получения координат глаз
    pev(visor, pev_view_ofs, start)
    xs_vec_add(start, origin, start)
     
    // Начало становится концом
    origin = end
     
    // Теперь у нас есть 2 важных вектора:
    // start - координаты глаз объекта visor
    // origin - коодинаты цели
     
    // Это используется один раз для обновления ignore_ent
    // Если точка видна, то возвращаем true
    if (is_point_visible_texture(start, origin, ignore_ent))
        return true
     
    // Получаем смещение обзора цели и добавляем их к координатам для получения координат глаз
    pev(target, pev_view_ofs, end)
    xs_vec_add(end, origin, end)
     
    // Если точка видна, возвращаем true
    if (is_point_visible(start, end, ignore_ent))
        return true
     
    // Проверяем точку оружия. Сначала проверяем, чтобы она не была равна нулю
    if (weapon_edge_point[get_user_weapon(target)] != 0.00)
    {
        // Получаем угол обзора и конвектируем в вектор
        pev(target, pev_v_angle, addict)
        angle_vector(addict, ANGLEVECTOR_FORWARD, addict)
        // Перемножаем их для получения головной точки оружия
        xs_vec_mul_scalar(addict, weapon_edge_point[get_user_weapon(target)], addict)
        // Добавляем их в конец координат для получения головной точки
        xs_vec_add(end, addict, end)
         
        // Если точка оружия видна, то возвращаем true
        if (is_point_visible(start, end, ignore_ent))
            return true
    }
     
    // Делаем вычитание
    xs_vec_sub(start, origin, normal)
     
    // У нас есть вектор:
    // normal - вектор между начальным вектором и координатами цели
     
    // Делаем проверку плоскости
    // Данные функции создадут плоскость, которая будет крутиться на основании позиции игрока
    // Проверять будем точки, которые сущестуют на плоскости
     
    // Нормализуем normal вектор
    // Это важно при проблемах с высотой
    xs_vec_mul_scalar(normal, 1.0/(xs_vec_len(normal)), normal)
    // Конвертируем в угловой вектор
    vector_to_angle(normal, plane_vec)
    // Создаем вектор, который перпендикулярен normal вектору
    angle_vector(plane_vec, ANGLEVECTOR_RIGHT, plane_vec)
     
    // Проверяем, находимся ли мы на том же уровне с целью
    if (floatabs(normal[2]) <= ANGLE_COS_HEIGHT_CHECK)
    {
        // Проверяем, присели ли цель
        // Создаем плоскость
        if (pev(target, pev_flags) & FL_DUCKING)
        {
            for (new i=0;i<4;i++)
            {
                if (i<2)
                {
                    for (new j=0;j<2;j++)
                    {
                        // Перемножаем вектора и добавляем к координатам для проверки видимости
                        // Тоже самое будет происходить и дальше
                        xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                        addict[2] = vec_add_height_crouch[j]
                        xs_vec_add(origin, addict, end)
                         
                        if (is_point_visible(start, end, ignore_ent))
                            return true
                    }
                }
                else
                {
                    for (new j=2;j<4;j++)
                    {
                        xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                        addict[2] = vec_add_height_crouch[j]
                        xs_vec_add(origin, addict, end)
                         
                        if (is_point_visible(start, end, ignore_ent))
                            return true
                    }
                }
            }
        }
        else
        {
            for (new i=0;i<4;i++)
            {
                if (i<2)
                {
                    for (new j=0;j<2;j++)
                    {
                        xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                        addict[2] = vec_add_height_standup[j]
                        xs_vec_add(origin, addict, end)
                         
                        if (is_point_visible(start, end, ignore_ent))
                            return true
                    }
                }
                else
                {
                    for (new j=2;j<4;j++)
                    {
                        xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                        addict[2] = vec_add_height_standup[j]
                        xs_vec_add(origin, addict, end)
                         
                        if (is_point_visible(start, end, ignore_ent))
                            return true
                    }
                }
            }
        }
    }
    else
    {
        // Единственное различие с предыдущим кодом - это использование normal вектора для перемещения точек вперед и назад
        // Проверяем выше ли мы игрока
        if (normal[2] > 0.0)
        {
            normal[2] = 0.0
            xs_vec_mul_scalar(normal, 1/(xs_vec_len(normal)), normal)
         
            if (pev(target, pev_flags) & FL_DUCKING)
            {
                for (new i=0;i<4;i++)
                {
                    if (i<2)
                    {
                        for (new j=0;j<2;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_crouch[j]
                            xs_vec_add(origin, addict, end)
                            xs_vec_mul_scalar(normal, (j == 0) ? (-GENERAL_X_Y_SORROUNDING) : (GENERAL_X_Y_SORROUNDING), addict)
                            xs_vec_add(end, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                    else
                    {
                        for (new j=2;j<4;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_crouch[j]
                            xs_vec_add(origin, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                }
            }
            else
            {
                for (new i=0;i<4;i++)
                {
                    if (i<2)
                    {
                        for (new j=0;j<2;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_standup[j]
                            xs_vec_add(origin, addict, end)
                            xs_vec_mul_scalar(normal, (j == 0) ? (-GENERAL_X_Y_SORROUNDING) : (GENERAL_X_Y_SORROUNDING), addict)
                            xs_vec_add(end, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                    else
                    {
                        for (new j=2;j<4;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_standup[j]
                            xs_vec_add(origin, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                }
            }
        }
        else
        {
            normal[2] = 0.0
            xs_vec_mul_scalar(normal, 1/(xs_vec_len(normal)), normal)
             
            if (pev(target, pev_flags) & FL_DUCKING)
            {
                for (new i=0;i<4;i++)
                {
                    if (i<2)
                    {
                        for (new j=0;j<2;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_crouch[j]
                            xs_vec_add(origin, addict, end)
                            xs_vec_mul_scalar(normal, (j == 0) ? GENERAL_X_Y_SORROUNDING : (-GENERAL_X_Y_SORROUNDING), addict)
                            xs_vec_add(end, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                    else
                    {
                        for (new j=2;j<4;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_crouch[j]
                            xs_vec_add(origin, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                }
            }
            else
            {
                for (new i=0;i<4;i++)
                {
                    if (i<2)
                    {
                        for (new j=0;j<2;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_standup[j]
                            xs_vec_add(origin, addict, end)
                            xs_vec_mul_scalar(normal, (j == 0) ? GENERAL_X_Y_SORROUNDING : (-GENERAL_X_Y_SORROUNDING), addict)
                            xs_vec_add(end, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                    else
                    {
                        for (new j=2;j<4;j++)
                        {
                            xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
                            addict[2] = vec_add_height_standup[j]
                            xs_vec_add(origin, addict, end)
                             
                            if (is_point_visible(start, end, ignore_ent))
                                return true
                        }
                    }
                }
            }
        }
    }
     
    // Цель не видна
    return false
}


7. Как увидеть точки, которые мы тестируем
нам нужно добавить данный код в плагин:
Код: Выделить всё

stock spr_bomb 

public plugin_precache
() 
{ 
    spr_bomb 
= precache_model("sprites/ledglow.spr") 
} 

stock bomb_led
(const Float:point[3]) 
{ 
    message_begin
(MSG_BROADCAST, SVC_TEMPENTITY) 
    write_byte
(TE_GLOWSPRITE) 
    engfunc
(EngFunc_WriteCoord, point[0]) 
    engfunc
(EngFunc_WriteCoord, point[1]) 
    engfunc
(EngFunc_WriteCoord, point[2]) 
    write_short
(spr_bomb) 
    write_byte
(1) 
    write_byte
(3) 
    write_byte
(255) 
    message_end
() 
}  

И изменить функции "is_point_visible", "is_point_visible_texture" на:
Код: Выделить всё

bool
:is_point_visible(const Float:start[3], const Float:point[3], ignore_ent) 
{ 
    bomb_led
(point) 
    return false 
} 

bool
:is_point_visible_texture(const Float:start[3], const Float:point[3], ignore_ent) 
{ 
    bomb_led
(point) 
    return false 
} 
 
Не пишите мне в ЛС: если вам нужна помощь на бесплатной основе. Любые вопросы на форум.
Аватара пользователя
DJ_WEST
Администратор
 
Сообщения: 3641
Зарегистрирован: 22 авг 2009, 00:38
Благодарил (а): 48 раз.
Поблагодарили: 2209 раз.
Опыт программирования: Больше трех лет
Языки программирования: Counter-Strike 1.6
Counter-Strike: Source
Left 4 Dead
Left 4 Dead 2

Re: Проверка виден ли игрок

Сообщение anarhist666 » 05 июл 2011, 01:56

Это может помочь от бага когда модели играков внезапно взмывают вверх?
Интерестно ,когда останеться последний сервер по cs 1.6 и чей он будет?!
Аватара пользователя
anarhist666
 
Сообщения: 18
Зарегистрирован: 16 дек 2010, 07:27
Благодарил (а): 5 раз.
Поблагодарили: 3 раз.
Опыт программирования: Около 3 месяцев
Языки программирования: Counter-Strike 1.6

Re: Проверка виден ли игрок

Сообщение Ser_UFL » 05 июл 2011, 12:49

anarhist666, вероятность стремится к нулю...
Возможно тебе поможет это: Вы должны зарегистрироваться, чтобы видеть ссылки.
Запомните, всегда по жизни вас будут красить вежливость и спокойствие, а не наезды и дешевые понты ;)
Аватара пользователя
Ser_UFL
 
Сообщения: 975
Зарегистрирован: 22 авг 2009, 19:30
Откуда: Hell
Благодарил (а): 276 раз.
Поблагодарили: 380 раз.
Языки программирования: Counter-Strike 1.6:
WebMod-scripts, little Pawn.


Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 9