Перейти к содержимому


Фотография

CNavMesh для L4D2


  • Авторизуйтесь для ответа в теме
Сообщений в теме: 14

#1 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 12 Апрель 2021 - 22:53

 есть адреса и разные функции для работы с Навмешем для TF 2

https://github.com/n...tf2.navmesh.txt

 

Может кто найти аналогичное для L4D2 ?



#2 dragokas

dragokas

    Постоянный пользователь

  • Пользователь
  • PipPipPipPipPip
  • 792 сообщений

Отправлено 13 Апрель 2021 - 16:37

Тебе прям все-все функции нужны, связанные с навигацией или что-то конкретное?

 

Я тебе уже на AM выкладывал форк плагина Внатуре, для визуализации ближайших зон.



#3 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 13 Апрель 2021 - 17:59

Тебе прям все-все функции нужны, связанные с навигацией или что-то конкретное?

 

Я тебе уже на AM выкладывал форк плагина Внатуре, для визуализации ближайших зон.

ты про devZone?


Тебе прям все-все функции нужны, связанные с навигацией или что-то конкретное?

 

 

Любые, которые способны вернуть любую координату на карте в радиусе игрока(коордитнаты)

Я просто не знаю какие вообще известны, в угле мало чего нашел.

 

 

Было бы неплохо так же узнать является ли координата местом куда нельзя зайти игроку, но где могут спотаться зараженные (может есть функция которая сама найдет ближайшую зону для спота заразы), к примеру в комнате здания на втором этаже как на финале карнавала где спотается танк и т.п.

 

Так же интересно как спотаются обычные зомби, какой у них алгоритм? они спотаются как за укрытием, так и на виду, может у зомби своя функция и алгоритм спавна? Может он общий для зомби и заразы? Жалко нету базы, так бы полазил может нашел что интересное...

 

 

то что я скинул у чела в геймдате есть такие функции

"CNavMesh::GetNearestNavAreaForPosition"
"CNavMesh::GetNearestNavAreaForEntity"

тоже интересные функи, пригодились бы..


Сообщение отредактировал Goldfish: 13 Апрель 2021 - 18:04


#4 dragokas

dragokas

    Постоянный пользователь

  • Пользователь
  • PipPipPipPipPip
  • 792 сообщений

Отправлено 13 Апрель 2021 - 19:58

ты про devZone?

 

 

Нет. https://forums.allie...733&postcount=4

 

 

 

Я просто не знаю какие вообще известны, в угле мало чего нашел.

 

А хорошо ли искал? Тебе на AM asherkin советует тоже, что и я, т.е. внимательно поизучать все Vector функции в списке функций, что они умеют.

А лучше сразу открыть vector.inc файл, и по некоторым даже сразу увидеть как они работают, какие вычисления проводятся.

 

 

 

Было бы неплохо так же узнать является ли координата местом куда нельзя зайти игроку, 

 

Как вариант, функция L4D2_IsReachable из left4dhooks direct.

 

 

"CNavMesh::GetNearestNavAreaForPosition"
"CNavMesh::GetNearestNavAreaForEntity"

тоже интересные функи, пригодились бы..

 

Это всё есть в Left4Dhooks Direct, если сделать свою обёртку, используя плагин выше, который тебе выложил + одну из:

 

L4D2Direct_GetTerrorNavArea

L4D_GetRandomPZSpawnPosition

L4D_GetNearestNavArea

L4D_FindRandomSpot

 

+ проверку ClientCanSeeClient, которую я тебе выкладывал в соседней теме.

 

Сам с ними не работал, т.к. не было необходимости. Но можешь посмотреть примеры в некоторых из плагинов Сильверса, например Anomaly.


Игра это делает вот так: 

ZombieManager *__cdecl z_spawn_old(const CCommand *a1)
{
  ZombieManager *result; // eax
  const IHandleEntity *v2; // ebx
  float v3; // xmm1_4
  float v4; // xmm0_4
  char *v5; // edx
  int v6; // eax
  char *v7; // eax
  int v8; // ebx
  bool v9; // di
  const char *v10; // eax
  const char *v11; // eax
  long double v12; // fst7
  int v13; // ebx
  float *v14; // eax
  __m128 v15; // xmm2
  float v16; // xmm0_4
  float v17; // xmm0_4
  float v18; // xmm0_4
  TerrorNavArea *v19; // eax
  TerrorNavArea *v20; // ebx
  int v21; // eax
  const char *v22; // eax
  CBaseEntity *v23; // edi
  CBaseEntity *v24; // eax
  ZombieManager *v25; // eax
  int v26; // edx
  int v27; // ebx
  CBaseEntity *v28; // edx
  ZombieManager *v29; // ebx
  int v30; // xmm0_4
  CBaseEdict *v31; // eax
  float v32; // eax
  ZombieManager *v33; // ebx
  float v34; // [esp+20h] [ebp-198h]
  float v35; // [esp+20h] [ebp-198h]
  float v36; // [esp+30h] [ebp-188h]
  float v37; // [esp+30h] [ebp-188h]
  float v38; // [esp+40h] [ebp-178h]
  float v39; // [esp+40h] [ebp-178h]
  CBaseEntity *v40; // [esp+40h] [ebp-178h]
  CBaseEntity *v41; // [esp+40h] [ebp-178h]
  CBaseEntity *v42; // [esp+40h] [ebp-178h]
  int v43; // [esp+58h] [ebp-160h]
  int v44; // [esp+5Ch] [ebp-15Ch]
  float v45; // [esp+60h] [ebp-158h]
  char *v46; // [esp+64h] [ebp-154h]
  char *v47; // [esp+64h] [ebp-154h]
  float v48; // [esp+68h] [ebp-150h]
  bool v49; // [esp+68h] [ebp-150h]
  float v50; // [esp+6Ch] [ebp-14Ch]
  bool v51; // [esp+6Ch] [ebp-14Ch]
  float v52; // [esp+70h] [ebp-148h] BYREF
  float v53; // [esp+74h] [ebp-144h]
  float v54; // [esp+78h] [ebp-140h]
  float v55; // [esp+80h] [ebp-138h]
  float v56; // [esp+84h] [ebp-134h]
  float v57; // [esp+88h] [ebp-130h]
  float v58; // [esp+90h] [ebp-128h]
  float v59; // [esp+94h] [ebp-124h]
  float v60; // [esp+98h] [ebp-120h]
  float v61; // [esp+A0h] [ebp-118h]
  float v62; // [esp+A4h] [ebp-114h]
  float v63; // [esp+A8h] [ebp-110h]
  int v64; // [esp+B0h] [ebp-108h]
  bool v65; // [esp+B4h] [ebp-104h]
  bool v66; // [esp+B5h] [ebp-103h]
  char v67[12]; // [esp+DCh] [ebp-DCh] BYREF
  int v68; // [esp+E8h] [ebp-D0h] BYREF
  float v69; // [esp+ECh] [ebp-CCh]
  float v70; // [esp+F0h] [ebp-C8h]
  float v71; // [esp+108h] [ebp-B0h]
  char v72; // [esp+113h] [ebp-A5h]
  CBaseEntity *v73; // [esp+130h] [ebp-88h] BYREF
  int v74; // [esp+134h] [ebp-84h]
  int v75; // [esp+138h] [ebp-80h]
  float v76; // [esp+140h] [ebp-78h] BYREF
  float v77; // [esp+144h] [ebp-74h]
  float v78; // [esp+148h] [ebp-70h]
  float v79; // [esp+14Ch] [ebp-6Ch] BYREF
  float v80; // [esp+150h] [ebp-68h]
  float v81; // [esp+154h] [ebp-64h]
  int v82[3]; // [esp+158h] [ebp-60h] BYREF
  int v83[3]; // [esp+164h] [ebp-54h] BYREF
  int v84; // [esp+170h] [ebp-48h] BYREF
  int v85; // [esp+174h] [ebp-44h]
  float v86; // [esp+178h] [ebp-40h]
  int v87; // [esp+17Ch] [ebp-3Ch] BYREF
  int v88; // [esp+180h] [ebp-38h]
  int v89; // [esp+184h] [ebp-34h]
  int v90[3]; // [esp+188h] [ebp-30h] BYREF
  int v91[9]; // [esp+194h] [ebp-24h] BYREF

  result = (ZombieManager *)UTIL_GetCommandClient();
  v2 = result;
  if ( !result )
    return result;
  v45 = *(float *)(dword_FE581C + 44);
  CBasePlayer::EyeVectors(result, (Vector *)&v76, 0, 0);
  v34 = v76 * 10000.0;
  v36 = v77 * 10000.0;
  v38 = 10000.0 * v78;
  (*(void (__cdecl **)(int *, const IHandleEntity *))(*(_DWORD *)v2 + 564))(v82, v2);
  v50 = *(float *)v82;
  v48 = *(float *)&v82[1];
  v46 = (char *)v82[2];
  (*(void (__stdcall **)(float *))(*(_DWORD *)v2 + 564))(&v79);
  v65 = 1;
  v52 = v79;
  v53 = v80;
  v54 = v81;
  v64 = 0;
  v55 = (float)(v34 - v79) + v50;
  v56 = (float)(v36 - v80) + v48;
  v57 = (float)(v38 - v81) + *(float *)&v46;
  v63 = 0.0;
  v62 = 0.0;
  v61 = 0.0;
  v60 = 0.0;
  v59 = 0.0;
  v58 = 0.0;
  v66 = (float)((float)((float)(v56 * v56) + (float)(v55 * v55)) + (float)(v57 * v57)) != 0.0;
  CTraceFilterSimple::CTraceFilterSimple((CTraceFilterSimple *)&v73, v2, 0, 0);
  (*(void (__stdcall **)(CBaseEntity *))(*(_DWORD *)enginetrace + 20))(enginetrace);
  if ( *(_DWORD *)(dword_F73E7C + 48) )
    DebugDrawLine((const Vector *)v67, (const Vector *)&v68, (Vector *)((char *)&dword_FC + 3), 0, 0, 1, -1.0);
  result = (ZombieManager *)dword_FE659C;
  v3 = *(float *)(dword_FE659C + 44);
  if ( v3 < 0.0 )
  {
    v4 = v71;
  }
  else
  {
    v35 = v76 * v3;
    v39 = v77 * v3;
    v37 = v3 * v78;
    (*(void (__stdcall **)(int *))(*(_DWORD *)v2 + 564))(v83);
    result = (ZombieManager *)dword_FE659C;
    v69 = v39 + *(float *)&v83[1];
    v4 = 0.000099999997 * *(float *)(dword_FE659C + 44);
    v70 = v37 + *(float *)&v83[2];
    *(float *)&v68 = v35 + *(float *)v83;
    v71 = v4;
  }
  if ( v4 == 1.0 )
    return result;
  if ( *(int *)a1 <= 1 )
  {
    v47 = "Common";
    v9 = 0;
    v44 = 0;
    v43 = -1;
    v51 = 0;
    v49 = 0;
    goto LABEL_28;
  }
  v5 = (char *)*((_DWORD *)a1 + 259);
  v47 = v5;
  if ( v5 == "mob" || !_V_stricmp("mob", v5) )
  {
    v21 = CDirector::GetCommonInfectedLimit(TheDirector);
    return (ZombieManager *)ZombieManager::SpawnMob(TheZombieManager, v21);
  }
  v44 = GetZombieClassFromName(v47);
  v6 = *(_DWORD *)a1;
  if ( *(int *)a1 <= 3 )
    goto LABEL_14;
  v7 = (char *)*((_DWORD *)a1 + 260);
  if ( v7 == "wound" )
    goto LABEL_46;
  if ( _V_stricmp(v7, "wound") )
  {
    v6 = *(_DWORD *)a1;
LABEL_14:
    v43 = -1;
    goto LABEL_15;
  }
  v22 = "";
  if ( *(int *)a1 > 3 )
LABEL_46:
    v22 = (const char *)*((_DWORD *)a1 + 261);
  v43 = strtol(v22, 0, 10);
  v6 = *(_DWORD *)a1;
LABEL_15:
  if ( v6 <= 2 )
  {
    v51 = 0;
    v9 = 0;
    v49 = 0;
  }
  else
  {
    v8 = 2;
    v51 = 0;
    v9 = 0;
    v49 = 0;
    do
    {
      if ( !v49 )
        v49 = _V_stricmp(*((const char **)a1 + v8 + 258), "auto") == 0;
      if ( !v9 )
      {
        v10 = "";
        if ( *(_DWORD *)a1 > v8 )
          v10 = (const char *)*((_DWORD *)a1 + v8 + 258);
        v9 = _V_stricmp(v10, "ragdoll") == 0;
      }
      if ( !v51 )
      {
        v11 = "";
        if ( *(_DWORD *)a1 > v8 )
          v11 = (const char *)*((_DWORD *)a1 + v8 + 258);
        v51 = _V_stricmp(v11, "area") == 0;
      }
      ++v8;
    }
    while ( *(_DWORD *)a1 > v8 );
  }
LABEL_28:
  v84 = v68;
  v86 = v45 + v70;
  *(float *)&v85 = v69;
  v12 = RandomFloat(0, 1135869952);
  v87 = 0;
  *(float *)&v88 = v12;
  v89 = 0;
  if ( g_bSpawnAtConstPos )
  {
    v84 = g_vConstSpawnPos;
    v85 = dword_FE6A6C;
    v86 = *(float *)&dword_FE6A70;
  }
  if ( g_bSpawnAtConstAng )
    v88 = g_flConstSpawnAng;
  if ( v49 && !(unsigned __int8)ZombieManager::GetRandomPZSpawnPosition(TheZombieManager, v44, 5, 0, (int)&v84) )
    return (ZombieManager *)Warning("Couldn't find a %s Spawn position in %d tries\n", v47, 5);
  v13 = (*(int (__cdecl **)(CCSGameRules *))(*(_DWORD *)g_pGameRules + 124))(g_pGameRules);
  v14 = (float *)(*(int (__cdecl **)(CCSGameRules *))(*(_DWORD *)g_pGameRules + 124))(g_pGameRules);
  v66 = 0;
  v55 = 0.0;
  v56 = 0.0;
  v57 = 0.0;
  v64 = 0;
  v61 = *(float *)(v13 + 24) - v14[3];
  v15 = (__m128)*(unsigned int *)(v13 + 28);
  v62 = v15.m128_f32[0] - v14[4];
  v16 = *(float *)(v13 + 32) - v14[5];
  v61 = v61 * 0.5;
  v62 = v62 * 0.5;
  v63 = v16 * 0.5;
  v15.m128_f32[0] = (float)((float)(v62 * v62) + (float)(v61 * v61)) + (float)(v63 * v63);
  v65 = _mm_unpacklo_ps(v15, v15).m128_f32[0] < 0.000001;
  v58 = *(float *)(v13 + 24) + v14[3];
  v59 = *(float *)(v13 + 28) + v14[4];
  v17 = *(float *)(v13 + 32) + v14[5];
  v52 = *(float *)&v84 + (float)(v58 * 0.5);
  v18 = v17 * 0.5;
  v53 = *(float *)&v85 + (float)(v59 * 0.5);
  v54 = v86 + v18;
  v58 = -(float)(v58 * 0.5);
  v59 = -(float)(v59 * 0.5);
  v60 = -v18;
  CTraceFilterSimple::CTraceFilterSimple((CTraceFilterSimple *)&v73, 0, 0, 0);
  (*(void (__cdecl **)(CBaseEntity *, float *, int, CBaseEntity **, char *))(*(_DWORD *)enginetrace + 20))(
    enginetrace,
    &v52,
    33702411,
    &v73,
    v67);
  if ( *(_DWORD *)(dword_F73E7C + 48) )
    DebugDrawLine((const Vector *)v67, (const Vector *)&v68, (Vector *)((char *)&dword_FC + 3), 255, 0, 1, -1.0);
  v19 = (TerrorNavArea *)CNavMesh::GetNearestNavArea(TheNavMesh, (const Vector *)&v84, 0, 10000.0, 0, 1, 0);
  v20 = v19;
  if ( (v72 || v71 < 1.0) && v19 )
  {
    TerrorNavArea::FindRandomSpot((TerrorNavArea *)v90);
    v84 = v90[0];
    v86 = v45 + *(float *)&v90[2];
    v85 = v90[1];
    if ( v44 != 7 )
      goto LABEL_50;
LABEL_39:
    if ( v9 )
      Warning("ragdoll flag is not implemented\n");
    s_ManualSpawn = 1;
    result = (ZombieManager *)ZombieManager::SpawnWitch((int)TheZombieManager, (Vector *)&v84, (int)&v87);
    s_ManualSpawn = 0;
    return result;
  }
  if ( v44 == 7 )
    goto LABEL_39;
LABEL_50:
  if ( v47 == "witch_bride" || !_V_stricmp(v47, "witch_bride") )
  {
    s_ManualSpawn = 1;
    result = (ZombieManager *)ZombieManager::SpawnWitchBride((int)TheZombieManager, (Vector *)&v84, (int)&v87);
    s_ManualSpawn = 0;
    return result;
  }
  if ( (unsigned int)(v44 - 1) > 5 && v44 != 8 )
  {
    s_ManualSpawn = 1;
    v28 = (CBaseEntity *)ZombieManager::SpawnCommonZombie(TheZombieManager, v20, &v84, 0);
    if ( v28 )
    {
      result = (ZombieManager *)v43;
      s_ManualSpawn = 0;
      if ( v43 >= 0 )
      {
        v40 = v28;
        result = (ZombieManager *)(*(int (__cdecl **)(CBaseEntity *))(*(_DWORD *)v28 + 332))(v28);
        v28 = v40;
        v29 = result;
        if ( result )
        {
          v30 = vec3_origin;
          if ( vec3_origin != *((float *)result + 1855)
            || dword_1011D6C != *((float *)result + 1856)
            || dword_1011D70 != *((float *)result + 1857) )
          {
            if ( *((_BYTE *)result + 108) )
            {
              *((_BYTE *)result + 112) |= 1u;
            }
            else
            {
              v31 = (CBaseEdict *)*((_DWORD *)result + 12);
              if ( v31 )
              {
                *(_DWORD *)v31 |= 0x101u;
                *(_WORD *)(CBaseEdict::GetChangeAccessor(v31) + 2) = 0;
                v28 = v40;
                v30 = vec3_origin;
              }
            }
            v32 = dword_1011D6C;
            *((_DWORD *)v29 + 1855) = v30;
            *((float *)v29 + 1856) = v32;
            *((float *)v29 + 1857) = dword_1011D70;
          }
          v41 = v28;
          result = (ZombieManager *)Infected::RequestWound(v29, v43);
          v28 = v41;
        }
      }
      if ( v9 )
      {
        v42 = v28;
        CTakeDamageInfo::CTakeDamageInfo((CTakeDamageInfo *)&v52, v28, v28, 10000.0, 1, 0);
        v73 = 0;
        v74 = 0;
        v75 = 1065353216;
        result = (ZombieManager *)(*(int (__cdecl **)(CBaseEntity *, float *, CBaseEntity **))(*(_DWORD *)v42 + 1244))(
                                    v42,
                                    &v52,
                                    &v73);
      }
      return result;
    }
    result = (ZombieManager *)CNavMesh::GetNearestNavArea(TheNavMesh, (const Vector *)&v84, 0, 10000.0, 0, 1, 0);
    v33 = result;
    if ( result )
    {
      TerrorNavArea::FindRandomSpot((TerrorNavArea *)v91);
      v84 = v91[0];
      v86 = v45 + *(float *)&v91[2];
      v85 = v91[1];
      result = (ZombieManager *)ZombieManager::SpawnCommonZombie(TheZombieManager, v33, &v84, 0);
    }
LABEL_64:
    s_ManualSpawn = 0;
    return result;
  }
  if ( v9 )
    Warning("ragdoll flag is not implemented");
  v73 = 0;
  v74 = 0;
  v75 = v44;
  ForEachTerrorPlayer<SpawnablePZScan>(&v73);
  if ( !v73 )
    goto LABEL_73;
  CCSPlayer::State_Transition(v73, 6);
  v23 = v73;
  if ( !v73 )
    goto LABEL_73;
  if ( !*(_DWORD *)(dword_FE57BC + 48) && v44 == 8 )
  {
    s_ManualSpawn = 1;
    goto LABEL_60;
  }
  if ( *(_DWORD *)(dword_F6511C + 48) )
  {
LABEL_73:
    s_ManualSpawn = 1;
    if ( v44 != 8 )
    {
      if ( v20 && v51 )
        result = (ZombieManager *)ZombieManager::SpawnSpecial(TheZombieManager, v44, v20, (int)&v87);
      else
        result = (ZombieManager *)ZombieManager::SpawnSpecial((int)TheZombieManager, v44, (Vector *)&v84, (int)&v87);
      goto LABEL_64;
    }
LABEL_60:
    if ( v20 && v51 )
      v24 = (CBaseEntity *)ZombieManager::SpawnTank(TheZombieManager, v20, (int)&v87, 0);
    else
      v24 = (CBaseEntity *)ZombieManager::SpawnTank((int)TheZombieManager, (Vector *)&v84, (int)&v87);
    result = (ZombieManager *)CDirector::TryOfferingTankBot(TheDirector, v24, 1);
    goto LABEL_64;
  }
  CCSPlayer::State_Transition(v73, 0);
  CBaseEntity::SetAbsOrigin(v23, (const Vector *)&v84);
  CBaseEntity::SetAbsAngles(v23, (int)&v87);
  if ( (*((_BYTE *)v23 + 333) & 8) != 0 )
    CBaseEntity::CalcAbsolutePosition(v23);
  v25 = TheZombieManager;
  *((_DWORD *)TheZombieManager + 159) = *((_DWORD *)v23 + 184);
  *((_DWORD *)v25 + 160) = *((_DWORD *)v23 + 185);
  v26 = *((_DWORD *)v23 + 186);
  *((_BYTE *)v25 + 648) = 1;
  *((_DWORD *)v25 + 161) = v26;
  *((_BYTE *)v23 + 16385) = 1;
  *((_DWORD *)v23 + 4097) = -1;
  *((_DWORD *)v23 + 4098) = -1082130432;
  g_ZombieClass = v44;
  (*(void (__cdecl **)(CBaseEntity *))(*(_DWORD *)v23 + 100))(v23);
  v27 = *(_DWORD *)(dword_FE56FC + 48);
  if ( v27 > 0 && v27 != *((_DWORD *)v23 + 64) )
  {
    (*(void (__cdecl **)(CBaseEntity *, int))(*(_DWORD *)v23 + 532))(v23, (int)v23 + 256);
    *((_DWORD *)v23 + 64) = v27;
  }
  (*(void (__cdecl **)(CBaseEntity *, int *, int *, void *))(*(_DWORD *)v23 + 476))(v23, &v84, &v87, &vec3_origin);
  result = TheZombieManager;
  *((_BYTE *)TheZombieManager + 648) = 0;
  return result;
}

Думаю, заметишь знакомые функции =))

По сути Silvers уже сделал за нас всю работу, собрав всё популярное вместе, обновив и сделав доступным для обоих сообществ.

Только бери читай доки, которые тоже в комплекте, и пользуйся :)


Так-то я в соседней теме уже выложил тебе чисто математические рассчёты, определения предмета в зоне обзора.

Там и GetAngleVectors(), которую ты давно ищешь и пытаешься составить вручную.

Это не прототип, а готовая рабочая функция.

Можешь добавить туда отладку в виде визуализации лучами от начальных до конечных точек, чтобы оценить как это работает.

Пример отладочных лучей, и партикля яркой точки (создаётся и удаляется через 5 сек.):

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

#define PARTICLE_LIGHT_GREEN "railroad_light_blink2" // green
#define PARTICLE_LIGHT_RED "weapon_pipebomb_blinking_light" // red bright

int g_iBeaconSprite;
bool g_bLeft4dead2;

public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
	g_bLeft4dead2 = (GetEngineVersion() == Engine_Left4Dead2);
	return APLRes_Success;
}

public void OnMapStart()
{
	if( g_bLeft4dead2 )
	{
		g_iBeaconSprite = PrecacheModel("materials/sprites/laserbeam.vmt");
	}
	else {
		g_iBeaconSprite = PrecacheModel("materials/sprites/laser.vmt");	
	}
	PrecacheGeneric("particles/environment_fx.pcf", true); 	// for PARTICLE_LIGHT_2 and PARTICLE_LIGHT_3
	PrecacheGeneric("particles/weapon_fx.pcf", true); 		// for PARTICLE_LIGHT_1
	PrecacheEffect("ParticleEffect");
	PrecacheParticleEffect(PARTICLE_LIGHT_RED);
	PrecacheParticleEffect(PARTICLE_LIGHT_GREEN);
}

stock int AddParticle(char s_Effect[100], float f_Origin[3], float time = 5.0)
{
	int i_Particle;
	
	i_Particle = CreateEntityByName("info_particle_system");
	
	if( i_Particle != -1 )
	{
		TeleportEntity(i_Particle, f_Origin, NULL_VECTOR, NULL_VECTOR);
		DispatchKeyValue(i_Particle, "effect_name", s_Effect);
		DispatchSpawn(i_Particle);
		ActivateEntity(i_Particle);
		AcceptEntityInput(i_Particle, "Start");
		SetEntityKillTimer(i_Particle, time);
	}
	return i_Particle;
}

stock void SetEntityKillTimer(int ent, float time)
{
	char sRemove[64];
	Format(sRemove, sizeof(sRemove), "OnUser1 !self:Kill::%f:1", time);
	SetVariantString(sRemove);
	AcceptEntityInput(ent, "AddOutput");
	AcceptEntityInput(ent, "FireUser1");
}

stock void LaserP(float vStart[3], float vEnd[3], float time = 5.0)
{
	TE_SetupBeamPoints(vStart, vEnd, g_iBeaconSprite, 0, 30, 0, time, g_bLeft4dead2 ? 1.0 : 3.0, g_bLeft4dead2 ? 1.0 : 3.0, 0, 0.0, {0, 0, 255, 128}, 0);
	TE_SendToAll();
}

stock void PrecacheEffect(const char[] sEffectName) // thanks to Dr. Api
{
    static int table = INVALID_STRING_TABLE;
    
    if (table == INVALID_STRING_TABLE)
    {
        table = FindStringTable("EffectDispatch");
    }
    bool save = LockStringTables(false);
    AddToStringTable(table, sEffectName);
    LockStringTables(save);
}

stock void PrecacheParticleEffect(const char[] sEffectName) // thanks to Dr. Api
{
    static int table = INVALID_STRING_TABLE;
    
    if (table == INVALID_STRING_TABLE)
    {
        table = FindStringTable("ParticleEffectNames");
    }
    bool save = LockStringTables(false);
    AddToStringTable(table, sEffectName);
    LockStringTables(save);
}

Использование:


LaserP(vStart, vEnd);
AddParticle(PARTICLE_LIGHT_GREEN, vEnd);


#5 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 13 Апрель 2021 - 21:36

Нет. https://forums.allie...733&postcount=4
 
 
 
А хорошо ли искал? Тебе на AM asherkin советует тоже, что и я, т.е. внимательно поизучать все Vector функции в списке функций, что они умеют.
А лучше сразу открыть vector.inc файл, и по некоторым даже сразу увидеть как они работают, какие вычисления проводятся.
 
 
 
Как вариант, функция L4D2_IsReachable из left4dhooks direct.
 
 
 
Это всё есть в Left4Dhooks Direct, если сделать свою обёртку, используя плагин выше, который тебе выложил + одну из:
 
L4D2Direct_GetTerrorNavArea
L4D_GetRandomPZSpawnPosition
L4D_GetNearestNavArea
L4D_FindRandomSpot
 
+ проверку ClientCanSeeClient, которую я тебе выкладывал в соседней теме.
 
Сам с ними не работал, т.к. не было необходимости. Но можешь посмотреть примеры в некоторых из плагинов Сильверса, например Anomaly.

Игра это делает вот так: 
 

Скрытый текст

Думаю, заметишь знакомые функции =))
По сути Silvers уже сделал за нас всю работу, собрав всё популярное вместе, обновив и сделав доступным для обоих сообществ.
Только бери читай доки, которые тоже в комплекте, и пользуйся :)

Так-то я в соседней теме уже выложил тебе чисто математические рассчёты, определения предмета в зоне обзора.
Там и GetAngleVectors(), которую ты давно ищешь и пытаешься составить вручную.
Это не прототип, а готовая рабочая функция.
Можешь добавить туда отладку в виде визуализации лучами от начальных до конечных точек, чтобы оценить как это работает.
Пример отладочных лучей, и партикля яркой точки (создаётся и удаляется через 5 сек.):
 
Скрытый текст

 

 
хорошо, большое спасибо


Сообщение отредактировал Goldfish: 14 Апрель 2021 - 13:56


#6 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 14 Апрель 2021 - 14:00

-


Сообщение отредактировал Goldfish: 14 Апрель 2021 - 14:07


#7 dragokas

dragokas

    Постоянный пользователь

  • Пользователь
  • PipPipPipPipPip
  • 792 сообщений

Отправлено 14 Апрель 2021 - 18:18

/**
 * @brief Given a vector position, returns the relative NavArea.
 * @remarks This is more reliable than L4D2Direct_GetTerrorNavArea.
 *
 * @param vecPos			The vector array to use to retrieve the NavArea
 *
 * @return					The NavArea value, or 0 on failure probably
 */
native int L4D_GetNearestNavArea(const float vecPos[3]);

/**
 * @brief Attempts to find a random valid position to spawn a Special Infected
 * @remarks The zombieClass does not matter but different values yield different results:
 * @remarks Using the Tank zombieClass probably searches for a larger area that's clear of objects
 *
 * @param client			Client id to find an area near this player. Accepts 0 to find a random area instead
 * @param zombieClass		Special Infected class to search for a spawn position for
 * @param attempts			How many tries to find a valid location
 * @param vecPos			The vector array to store the valid location on success
 *
 * @return					True on success, false on failure to find valid location
 */
native bool L4D_GetRandomPZSpawnPosition(int client, int zombieClass, int attempts, float vecPos[3]);

/**
 * @brief Given a nav area value, returns a randomly selected position for spawning.
 * @remarks This is what Witches use to spawn.
 *
 * @param NavArea			The NavArea to search for a valid location
 * @param vecPos			The vector array to store the valid location on success
 *
 * @noreturn
 */
native void L4D_FindRandomSpot(int NavArea, float vecPos[3]);

L4D_GetRandomPZSpawnPosition отвечает за поиск рандомной позиции для спауна заразы конкретного размера в области NavArea, на которой находится указанная точка.

Но если область NavArea слишком мала, то разброс рандомных позиций тоже будет минимален, либо вообще невозможен из-за коллизий.

 

Как вариант, для расширения зоны охвата можно находить ближайшие NavArea к текущему NavArea и применения L4D_GetRandomPZSpawnPosition уже к ним.

Текущий NavArea, на котором находится игрок можешь узнать, получив координату игрока через GetClientAbsOrigin и затем вызвав L4D_GetNearestNavArea.

Ближайшие NavArea можно найти:

 - либо через перечисление всех зон и определения расстояния между ними (через GetVectorDistance) - так сделано в плагине, который я тебе давал на AM.

 - либо через прибавление (отнимание) какой-нибудь константы расстояния поочередно к осям X и/или Z от текущей позиции игрока и нахождения NavArea каждой из этих позиций через L4D_GetNearestNavArea

Затем необходимо проверить флаг этой NavArea, чтобы она была доступна для спауна в ней заразы. Какой из флагов тебе нужен я не знаю, но можно выяснить опытным путём.

Флаг можно извлечь получив адрес в памяти NavArea через L4D2Direct_GetTerrorNavArea и потом добавив оффсет - какой, можно посмотреть в плагине Внатуре.

 

Вместо использования L4D_GetRandomPZSpawnPosition (которая я бы не сказал, что отличается надёжностью, т.к. постоянно сыпет в логах сервера "Can't spawn <class> in 5 tries"),

можно написать свою альтернативу, к примеру: в цикле по нужной зоне вызываешь L4D_FindRandomSpot и затем проверяешь, подходит ли тебе эта точка, например, через TraceHull, отвечающий есть ли коллизия в конкретном месте для размеров коробки с указанными размерами:

bool IsStuckPos(float vPos[3]) // check if the position applicable to respawn a client of a given size without collision
{
	float vMin[3], vMax[3];
	Handle hTrace;
	bool bHit;
	GetClientSize(vMin, vMax);
	hTrace = TR_TraceHullFilterEx(vPos, vPos, vMin, vMax, MASK_PLAYERSOLID, TraceRay_NoPlayers);
	if( hTrace ) {
		bHit = TR_DidHit(hTrace);
		delete hTrace;
	}
	return bHit;
}

public bool TraceRay_NoPlayers(int entity, int contentsMask)
{
	return (entity > MaxClients);
}

void GetClientSize(float vMin[3], float vMax[3])
{
	vMin[0] = -16.0; 	vMin[1] = -16.0; 	vMin[2] = 0.0;
	vMax[0] = 16.0; 	vMax[1] = 16.0; 	vMax[2] = 71.0;
	
	// Warning: GetClientMins & GetClientMaxs are not reliable when applied to dead or spectator, so we are using pre-defined values
}

  • Goldfish это нравится

#8 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 14 Апрель 2021 - 20:31

спасибо за инфу
 
/*
            *  TerrorNavArea::FindRandomSpot(TerrorNavArea *this)
            */
            "FindRandomSpot"
            {
                "library"    "server"
                "linux"        "@_ZNK13TerrorNavArea14FindRandomSpotEv"
                "windows"    "\x55\x8B\x2A\xF3\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x83\x2A\x2A\x56\x8B\x2A\xF3\x2A\x2A\x2A\x2A\xF3\x2A\x2A\x2A\x2A\x0F"
                /* 55 8B ? F3 ? ? ? ? ? ? ? 83 ? ? 56 8B ? F3 ? ? ? ? F3 ? ? ? ? 0F */
                /* Search "Couldn't find a %s Spawn position in %d tries\n", some calls below */
            }
а разве функция TerrorNavArea::FindRandomSpot принимает еще какой аргумент кроме указателя на TerrorNavArea ?
в left4d hooks direct в нее передаются координаты аргументом (вроде)
	StartPrepSDKCall(SDKCall_Raw);
	if( PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "FindRandomSpot") == false )
	{
		LogError("Failed to find signature: FindRandomSpot");
	} else {
		PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef, _, VENCODE_FLAG_COPYBACK);
		g_hSDK_Call_FindRandomSpot = EndPrepSDKCall();
		if( g_hSDK_Call_FindRandomSpot == null )
			LogError("Failed to create SDKCall: FindRandomSpot");
	}
ну и собственно сервер крашит от нее
 
в  прочем отписал в теме автора https://forums.allie...4&postcount=289
боюсь пока что к сожалению не смогу использовать функцию.
 
я не знаю может просто нужно убрать передачу аргумента в функцию в коде плагина для проверки, но я не разбираюсь как это делать.

Сообщение отредактировал Goldfish: 14 Апрель 2021 - 20:35


#9 dragokas

dragokas

    Постоянный пользователь

  • Пользователь
  • PipPipPipPipPip
  • 792 сообщений

Отправлено 14 Апрель 2021 - 21:17

спасибо за инфу 

 

Можно не благодарить, т.к. это только в теории (судя по описанию). Я ничего из этого не использовал.

 

 

 

в  прочем отписал в теме автора https://forums.allie...4&postcount=289

 

 

Как он проверит, если ты даже ни код не выложил, ни название игры.

 

            *  TerrorNavArea::FindRandomSpot(TerrorNavArea *this)

 

Это прототип из основного окна IDA.

В дезассемблированном виде, она выглядит:

TerrorNavArea *__stdcall TerrorNavArea::FindRandomSpot(TerrorNavArea *this, CNavArea *a2)

что тоже не факт, что верно. Она часто может ошибаться.

 

Но исходя из кода функции, склонен к тому, что это правильный вариант.

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

TerrorNavArea *__stdcall TerrorNavArea::FindRandomSpot(TerrorNavArea *this, CNavArea *a2)
{
  TerrorNavArea *result; // eax
  float v3; // xmm2_4
  float v4; // xmm0_4
  float v5; // xmm0_4
  int v6; // edx
  float v7; // xmm4_4
  float v8; // xmm0_4
  float v9; // [esp+1Ch] [ebp-4Ch]
  float v10; // [esp+1Ch] [ebp-4Ch]
  float v11; // [esp+40h] [ebp-28h]

  result = this;
  v3 = *((float *)a2 + 1);
  v4 = *((float *)a2 + 4) - v3;
  if ( v4 < 50.0 || (float)(*((float *)a2 + 5) - *((float *)a2 + 2)) < 50.0 )
  {
    v5 = *((float *)a2 + 13) + 10.0;
    *(_DWORD *)this = *((_DWORD *)a2 + 11);
    v6 = *((_DWORD *)a2 + 12);
    *((float *)this + 2) = v5;
    *((_DWORD *)this + 1) = v6;
  }
  else
  {
    v9 = RandomFloat(0, v4 - 50.0);
    v7 = *((float *)a2 + 2);
    v8 = *((float *)a2 + 5);
    *(float *)this = (float)(v3 + 25.0) + v9;
    v11 = (float)(v3 + 25.0) + v9;
    v10 = RandomFloat(0, (float)(v8 - v7) - 50.0);
    *((float *)this + 1) = (float)(v7 + 25.0) + v10;
    CNavArea::GetZ(a2, v11, (float)(v7 + 25.0) + v10);
    result = this;
    *((float *)this + 2) = v10 + 10.0;
  }
  return result;
}

А вот так она выглядит в L4D1:

TerrorNavArea *__stdcall TerrorNavArea::FindRandomSpot(TerrorNavArea *this, int a2)
{
  TerrorNavArea *result; // eax

  if ( *(float *)(a2 + 16) - *(float *)(a2 + 4) < 50.0 || *(float *)(a2 + 20) - *(float *)(a2 + 8) < 50.0 )
  {
    *(_DWORD *)this = *(_DWORD *)(a2 + 44);
    *((_DWORD *)this + 1) = *(_DWORD *)(a2 + 48);
    *((float *)this + 2) = *(float *)(a2 + 52) + 10.0;
    result = this;
  }
  else
  {
    TerrorNavArea::FindRandomSpot();
    result = this;
  }
  return result;
}

a2 очевидно та же CNavArea.

А вот результат всё таки возвращается только как возвращаемое функцией значение, аргумент не изменяется и явно не похож на float[3].

 

В любом случае, поиск случайной точки на поверхности четырёхугольника в пространстве - это одна из простейших задачек школьного курса.


Сообщение отредактировал dragokas: 14 Апрель 2021 - 21:24


#10 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 14 Апрель 2021 - 22:37

в общем методом проб и ошибок поэкспериментировал
 
вместо
 PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef, _, VENCODE_FLAG_COPYBACK);
прописал
PrepSDKCall_SetReturnInfo(SDKType_Float, SDKPass_Plain);
 

Скрытый текст

 
не уверен что правильно но функция заработала и стала возвращать float[3]
напишу в теме автору
       
 
Скрытый текст


Сообщение отредактировал Goldfish: 14 Апрель 2021 - 22:45


#11 dragokas

dragokas

    Постоянный пользователь

  • Пользователь
  • PipPipPipPipPip
  • 792 сообщений

Отправлено 14 Апрель 2021 - 22:47

И на сколько корректные данные возвращаются?

 

На AM [ spoiler ] [ /spoiler ] как и везде.


Сообщение отредактировал dragokas: 14 Апрель 2021 - 22:47


#12 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 14 Апрель 2021 - 22:58

И на сколько корректные данные возвращаются?

 

На AM [ spoiler ] [ /spoiler ] как и везде.

корректные, потелепортировался на эти координаты, все хорошо, только эти координаты сильно близко выходят если искать позицию с позиции игрока)

 

dragokas не можешь еще глянуть FindRandomSpot используется в FindNearbySpawnSpot?

Может тогда нет смысла пытаться писать аналог FindNearbySpawnSpot результат который будет не лучше этой функции?


Сообщение отредактировал Goldfish: 14 Апрель 2021 - 23:26


#13 dragokas

dragokas

    Постоянный пользователь

  • Пользователь
  • PipPipPipPipPip
  • 792 сообщений

Отправлено 14 Апрель 2021 - 23:51

корректные, потелепортировался на эти координаты, все хорошо, 

 

По видимому тебе очень сильно повезло, и эти координаты хранились в первом из полей структуры TerrorNavArea, поэтому получив ее адрес ты сразу получаешь корректные данные в float[3].

Но это только предположение.

 

 только эти координаты сильно близко выходят если искать позицию с позиции игрока)

 

 

да, они обычно очень небольшие, поэтому необходимо искать индексы соседних, а то и дальше.

 

dragokas не можешь еще глянуть FindRandomSpot используется в FindNearbySpawnSpot?

 

Довольно любопытная функция.

 

int __usercall FindNearbySpawnSpot@<eax>(long double a1@<st0>, CTerrorPlayer *a2, Vector *a3, int a4, bool a5, float a6)

Сигнатура: _Z19FindNearbySpawnSpotP13CTerrorPlayerP6Vectoribf


Она использует:

CTraceFilterSimple::CTraceFilterSimple
CNavMesh::ForAllAreasOverlappingExtent<CollectRespawnAreas>
CNavMesh::GetNearestNavArea
TerrorNavArea::FindRandomSpot
NavAreaTravelDistance<ShortestPathCost>
CNavArea::IsBlocked
Вот только я не знаю как по простому такие вызывать, она использует нестандартное соглашение. 1-й аргумент из регистра. Можно конечно, просто забить, но тогда туда попадёт мусор.
 
Её вызывает GetSafeSpawnPos. Содержимое, думаю, будет тебе  любопытно:
float *__usercall GetSafeSpawnPos@<eax>(float *a1@<eax>, CTerrorPlayer *a2@<edx>, long double a3@<st0>, float a4, float a5, float a6, char a7, int a8, int a9)
{
  const char *v11; // eax
  const Vector *v12; // eax
  const char *v13; // eax
  int v15; // eax
  int v16; // eax
  float *v17; // eax
  int v18; // xmm0_4
  CDirectorTacticalServices *v19; // eax
  Vector *v20; // [esp+24h] [ebp-254h]
  char v21; // [esp+2Bh] [ebp-24Dh]
  int v22; // [esp+30h] [ebp-248h] BYREF
  char v23[263]; // [esp+35h] [ebp-243h] BYREF
  int v24; // [esp+13Ch] [ebp-13Ch] BYREF
  char v25[263]; // [esp+141h] [ebp-137h] BYREF
  int v26[3]; // [esp+248h] [ebp-30h] BYREF
  int v27[9]; // [esp+254h] [ebp-24h] BYREF

  *(float *)v26 = a4;
  v21 = a7;
  *(float *)&v26[1] = a5;
  *(float *)&v26[2] = a6;
  if ( *((_BYTE *)TheDirector + 463) && (v15 = TerrorNavMesh::GetInitialCheckpoint(TheNavMesh)) != 0 )
  {
    Checkpoint::GetSpawnPosition(v15, v26, a9, 0);
    if ( a8 != 3 )
      goto LABEL_3;
  }
  else if ( a8 != 3 )
  {
    goto LABEL_3;
  }
  v16 = CNavMesh::GetNearestNavArea(TheNavMesh, (const Vector *)&a4, 0, 10000.0, 0, 1, 0);
  if ( v16 )
  {
    if ( *(_BYTE *)(v16 + 125) )
    {
      if ( (unsigned __int8)CDirectorTacticalServices::IsVisibleToTeam(
                              *((CDirectorTacticalServices **)TheDirector + 346),
                              (const Vector *)&a4,
                              2,
                              0,
                              0.0,
                              0,
                              0)
        || (v17 = (float *)(*(int (__cdecl **)(CCSGameRules *))(*(_DWORD *)g_pGameRules + 124))(g_pGameRules),
            *(float *)v27 = *v17 + a4,
            *(float *)&v27[1] = v17[1] + a5,
            *(float *)&v18 = v17[2] + a6,
            v19 = (CDirectorTacticalServices *)*((_DWORD *)TheDirector + 346),
            v27[2] = v18,
            (unsigned __int8)CDirectorTacticalServices::IsVisibleToTeam(v19, (const Vector *)v27, 2, 0, 0.0, 0, 0)) )
      {
        if ( !(unsigned __int8)FindNearbySpawnSpot(a3, a2, (Vector *)v26, 3, v21, 1000.0)
          && !(unsigned __int8)FindNearbySpawnSpot(a3, a2, (Vector *)v26, 3, v21, 2000.0)
          && !(unsigned __int8)FindNearbySpawnSpot(a3, a2, (Vector *)v26, 3, v21, 3000.0) )
        {
          if ( (unsigned __int8)FindNearbySpawnSpot(a3, a2, (Vector *)v26, 3, v21, 10000.0) )
            qmemcpy(a1, v26, 0xCu);
          else
            qmemcpy(a1, &vec3_origin, 0xCu);
          return a1;
        }
        goto LABEL_21;
      }
    }
  }
LABEL_3:
  if ( *(float *)v26 <= -0.0099999998
    || *(float *)v26 >= 0.0099999998
    || *(float *)&v26[1] <= -0.0099999998
    || *(float *)&v26[1] >= 0.0099999998
    || *(float *)&v26[2] <= -0.0099999998
    || *(float *)&v26[2] >= 0.0099999998 )
  {
    if ( v21 )
    {
      v20 = (Vector *)((*(int (__cdecl **)(CCSGameRules *))(*(_DWORD *)g_pGameRules + 124))(g_pGameRules) + 48);
      v12 = (const Vector *)((*(int (__cdecl **)(CCSGameRules *))(*(_DWORD *)g_pGameRules + 124))(g_pGameRules) + 36);
    }
    else
    {
      v20 = (Vector *)((*(int (__cdecl **)(CCSGameRules *))(*(_DWORD *)g_pGameRules + 124))(g_pGameRules) + 24);
      v12 = (const Vector *)((*(int (__cdecl **)(CCSGameRules *))(*(_DWORD *)g_pGameRules + 124))(g_pGameRules) + 12);
    }
    if ( (unsigned __int8)UTIL_CanEntityFit(a2, (const Vector *)v26, v12, v20) )
    {
      CFmtStrN<256>::CFmtStrN((int)&v22, "(%f, %f, %f)", a4, a5, a6);
      v13 = "BOT";
      if ( a2 )
        v13 = (const char *)(*(int (__cdecl **)(CTerrorPlayer *))(*(_DWORD *)a2 + 184))(a2);
      UTIL_LogPrintf("GetSafeSpawnPos: %s ideal pos %s is clear\n", v13, v23);
      goto LABEL_21;
    }
  }
  if ( (unsigned __int8)FindNearbySpawnSpot(a3, a2, (Vector *)v26, a8, v21, 150.0)
    || (unsigned __int8)FindNearbySpawnSpot(a3, a2, (Vector *)v26, a8, v21, 1000.0) )
  {
LABEL_21:
    *a1 = *(float *)v26;
    a1[1] = *(float *)&v26[1];
    a1[2] = *(float *)&v26[2];
    return a1;
  }
  CFmtStrN<256>::CFmtStrN((int)&v24, "(%f, %f, %f)", a4, a5, a6);
  v11 = "BOT";
  if ( a2 )
    v11 = (const char *)(*(int (__cdecl **)(CTerrorPlayer *))(*(_DWORD *)a2 + 184))(a2);
  UTIL_LogPrintf("GetSafeSpawnPos: Spawning %s stuck at %s - couldn't find a nearby empty space\n", v11, v25);
  if ( a8 == 3 )
  {
    *(_DWORD *)a1 = vec3_origin;
    a1[1] = dword_10162CC;
    a1[2] = dword_10162D0;
  }
  else
  {
    *a1 = a4;
    a1[1] = a5;
    a1[2] = a6;
  }
  return a1;
}


#14 Goldfish

Goldfish

    Пользователь

  • Пользователь
  • PipPipPip
  • 388 сообщений

Отправлено 15 Апрель 2021 - 1:15

    if ( a8 != 3 )
      goto LABEL_3;
  }
  else if ( a8 != 3 )
  {
    goto LABEL_3;
  }

панимаю)

 

Спасибо за код, теперь кажется понимаю как игра пытается спавнить зараженных.

 

она вызывает FindNearbySpawnSpot с классом типа TEAM_INFECTED 3

 

я тоже пытался с 3 классом вызывать, но в большинстве случаев неудачно.

Я пытаюсь найти спот рядом, а не супер скрытом месте как это делает игра, поэтому использую класс 1 или 0, тогда функция быстро находит координату причем в любом радиусе, затем я вручную отфильтровываю есть ли преграда между спотом и игроком или видит ли он этут точку, боюсь движек отфильтровывает этот процесс  скрупулезно, чтобы точка была идеальная и с укрытия.

 

Думаю этой информации уже достаточно )


Сообщение отредактировал Goldfish: 15 Апрель 2021 - 1:34


#15 dragokas

dragokas

    Постоянный пользователь

  • Пользователь
  • PipPipPipPipPip
  • 792 сообщений

Отправлено 15 Апрель 2021 - 1:48

Видимо аналогично тому, как когда ты пытаешься заспавниться вручную заразой в версусе, а тебе игра не дает подойти через чур близко.

 

Попробуй ещё класс 2 TEAM_SURVIVORS.

Если там учитывается, то заодно отфильтрует разного рода коллизии, чтобы не застрять. Ну а так, как проверять это вручную ты уже знаешь ))




Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных