есть адреса и разные функции для работы с Навмешем для TF 2
https://github.com/n...tf2.navmesh.txt
Может кто найти аналогичное для L4D2 ?
Отправлено 12 Апрель 2021 - 22:53
есть адреса и разные функции для работы с Навмешем для TF 2
https://github.com/n...tf2.navmesh.txt
Может кто найти аналогичное для L4D2 ?
Отправлено 13 Апрель 2021 - 16:37
Тебе прям все-все функции нужны, связанные с навигацией или что-то конкретное?
Я тебе уже на AM выкладывал форк плагина Внатуре, для визуализации ближайших зон.
Отправлено 13 Апрель 2021 - 17:59
Тебе прям все-все функции нужны, связанные с навигацией или что-то конкретное?
Я тебе уже на AM выкладывал форк плагина Внатуре, для визуализации ближайших зон.
ты про devZone?
Тебе прям все-все функции нужны, связанные с навигацией или что-то конкретное?
Любые, которые способны вернуть любую координату на карте в радиусе игрока(коордитнаты)
Я просто не знаю какие вообще известны, в угле мало чего нашел.
Было бы неплохо так же узнать является ли координата местом куда нельзя зайти игроку, но где могут спотаться зараженные (может есть функция которая сама найдет ближайшую зону для спота заразы), к примеру в комнате здания на втором этаже как на финале карнавала где спотается танк и т.п.
Так же интересно как спотаются обычные зомби, какой у них алгоритм? они спотаются как за укрытием, так и на виду, может у зомби своя функция и алгоритм спавна? Может он общий для зомби и заразы? Жалко нету базы, так бы полазил может нашел что интересное...
то что я скинул у чела в геймдате есть такие функции
"CNavMesh::GetNearestNavAreaForPosition" "CNavMesh::GetNearestNavAreaForEntity"
тоже интересные функи, пригодились бы..
Сообщение отредактировал Goldfish: 13 Апрель 2021 - 18:04
Отправлено 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);
Отправлено 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
Отправлено 14 Апрель 2021 - 14:00
-
Сообщение отредактировал Goldfish: 14 Апрель 2021 - 14:07
Отправлено 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 }
Отправлено 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 ?
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"); }ну и собственно сервер крашит от нее
Сообщение отредактировал Goldfish: 14 Апрель 2021 - 20:35
Отправлено 14 Апрель 2021 - 21:17
спасибо за инфу
Можно не благодарить, т.к. это только в теории (судя по описанию). Я ничего из этого не использовал.
Как он проверит, если ты даже ни код не выложил, ни название игры.
* 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
Отправлено 14 Апрель 2021 - 22:37
в общем методом проб и ошибок поэкспериментировал
вместо
PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef, _, VENCODE_FLAG_COPYBACK);
прописал
PrepSDKCall_SetReturnInfo(SDKType_Float, SDKPass_Plain);
Сообщение отредактировал Goldfish: 14 Апрель 2021 - 22:45
Отправлено 14 Апрель 2021 - 22:47
И на сколько корректные данные возвращаются?
На AM [ spoiler ] [ /spoiler ] как и везде.
Сообщение отредактировал dragokas: 14 Апрель 2021 - 22:47
Отправлено 14 Апрель 2021 - 22:58
И на сколько корректные данные возвращаются?
На AM [ spoiler ] [ /spoiler ] как и везде.
корректные, потелепортировался на эти координаты, все хорошо, только эти координаты сильно близко выходят если искать позицию с позиции игрока)
dragokas не можешь еще глянуть FindRandomSpot используется в FindNearbySpawnSpot?
Может тогда нет смысла пытаться писать аналог FindNearbySpawnSpot результат который будет не лучше этой функции?
Сообщение отредактировал Goldfish: 14 Апрель 2021 - 23:26
Отправлено 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
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; }
Отправлено 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 Апрель 2021 - 1:48
Видимо аналогично тому, как когда ты пытаешься заспавниться вручную заразой в версусе, а тебе игра не дает подойти через чур близко.
Попробуй ещё класс 2 TEAM_SURVIVORS.
Если там учитывается, то заодно отфильтрует разного рода коллизии, чтобы не застрять. Ну а так, как проверять это вручную ты уже знаешь ))
0 пользователей, 0 гостей, 0 анонимных