Guest User

level_bullet_manager_firetrace.cpp

a guest
Sep 7th, 2016
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.00 KB | None | 0 0
  1. // Level_Bullet_Manager.cpp: для обеспечения полета пули по траектории
  2. // все пули и осколки передаются сюда
  3. // (для просчета столкновений и их визуализации)
  4. //////////////////////////////////////////////////////////////////////
  5.  
  6. #include "stdafx.h"
  7. #include "Level_Bullet_Manager.h"
  8. #include "entity.h"
  9. #include "../xrEngine/gamemtllib.h"
  10. #include "level.h"
  11. #include "gamepersistent.h"
  12. #include "game_cl_base.h"
  13. #include "xrmessages.h"
  14. #include "../Include/xrRender/Kinematics.h"
  15. #include "Actor.h"
  16. #include "AI/Stalker/ai_stalker.h"
  17. #include "character_info.h"
  18. #include "game_cl_base_weapon_usage_statistic.h"
  19. #include "../xrcdb/xr_collide_defs.h"
  20. #include "../xrengine/xr_collide_form.h"
  21. #include "weapon.h"
  22. #include "ik/math3d.h"
  23. #include "actor.h"
  24. #include "ai/monsters/basemonster/base_monster.h"
  25.  
  26. //константы ShootFactor, определяющие
  27. //поведение пули при столкновении с объектом
  28. #define RICOCHET_THRESHOLD 0.1
  29. #define STUCK_THRESHOLD 0.4
  30.  
  31. //расстояния не пролетев которого пуля не трогает того кто ее пустил
  32. extern float gCheckHitK;
  33.  
  34. //test callback функция
  35. // object - object for testing
  36. //return TRUE-тестировать объект / FALSE-пропустить объект
  37. BOOL CBulletManager::test_callback(const collide::ray_defs& rd, CObject* object, LPVOID params)
  38. {
  39. if (!object)
  40. return TRUE;
  41.  
  42. bullet_test_callback_data* pData = (bullet_test_callback_data*)params;
  43. SBullet* bullet = pData->pBullet;
  44.  
  45. if( (object->ID() == bullet->parent_id) &&
  46. (bullet->fly_dist<parent_ignore_distance) &&
  47. (!bullet->flags.ricochet_was)) return FALSE;
  48.  
  49. BOOL bRes = TRUE;
  50. CEntity* entity = smart_cast<CEntity*>(object);
  51. if (entity&&entity->g_Alive()&&(entity->ID()!=bullet->parent_id)){
  52. ICollisionForm* cform = entity->collidable.model;
  53. if ((NULL!=cform) && (cftObject==cform->Type())){
  54. CActor* actor = smart_cast<CActor*>(entity);
  55. CAI_Stalker* stalker= smart_cast<CAI_Stalker*>(entity);
  56. // в кого попали?
  57. if (actor && IsGameTypeSingle()/**/||stalker/**/){
  58. // попали в актера или сталкера
  59. Fsphere S = cform->getSphere();
  60. entity->XFORM().transform_tiny (S.P) ;
  61. float dist = rd.range;
  62. // проверим попали ли мы в описывающую сферу
  63. if (Fsphere::rpNone!=S.intersect_full(bullet->bullet_pos, bullet->dir, dist))
  64. {
  65. // да попали, найдем кто стрелял
  66. bool play_whine = true;
  67. CObject* initiator = Level().Objects.net_Find (bullet->parent_id);
  68. if (actor){
  69. // попали в актера
  70. float hpf = 1.f;
  71. float ahp = actor->HitProbability();
  72. #if 1
  73. # if 0
  74. CObject *weapon_object = Level().Objects.net_Find (bullet->weapon_id);
  75. if (weapon_object) {
  76. CWeapon *weapon = smart_cast<CWeapon*>(weapon_object);
  77. if (weapon) {
  78. float fly_dist = bullet->fly_dist+dist;
  79. float dist_factor = _min(1.f,fly_dist/Level().BulletManager().m_fHPMaxDist);
  80. ahp = dist_factor*weapon->hit_probability() + (1.f-dist_factor)*1.f;
  81. }
  82. }
  83. # else
  84. float game_difficulty_hit_probability = actor->HitProbability();
  85. CAI_Stalker *stalker = smart_cast<CAI_Stalker*>(initiator);
  86. if (stalker)
  87. hpf = stalker->SpecificCharacter().hit_probability_factor();
  88.  
  89. float dist_factor = 1.f;
  90. CObject *weapon_object = Level().Objects.net_Find (bullet->weapon_id);
  91. if (weapon_object) {
  92. CWeapon *weapon = smart_cast<CWeapon*>(weapon_object);
  93. if (weapon) {
  94. game_difficulty_hit_probability = weapon->hit_probability();
  95. float fly_dist = bullet->fly_dist+dist;
  96. dist_factor = _min(1.f,fly_dist/Level().BulletManager().m_fHPMaxDist);
  97. }
  98. }
  99.  
  100. ahp = dist_factor*game_difficulty_hit_probability + (1.f-dist_factor)*1.f;
  101. # endif
  102. #else
  103. CAI_Stalker* i_stalker = smart_cast<CAI_Stalker*>(initiator);
  104. // если стрелял сталкер, учитываем - hit_probability_factor сталкерa иначе - 1.0
  105. if (i_stalker) {
  106. hpf = i_stalker->SpecificCharacter().hit_probability_factor();
  107. float fly_dist = bullet->fly_dist+dist;
  108. float dist_factor = _min(1.f,fly_dist/Level().BulletManager().m_fHPMaxDist);
  109. ahp = dist_factor*actor->HitProbability() + (1.f-dist_factor)*1.f;
  110. }
  111. #endif
  112. if (Random.randF(0.f,1.f)>(ahp*hpf)){
  113. bRes = FALSE; // don't hit actor
  114. play_whine = true; // play whine sound
  115. }else{
  116. // real test actor CFORM
  117. Level().BulletManager().m_rq_results.r_clear();
  118.  
  119. if (cform->_RayQuery(rd,Level().BulletManager().m_rq_results)){
  120. bRes = TRUE; // hit actor
  121. play_whine = false; // don't play whine sound
  122. }else{
  123. bRes = FALSE; // don't hit actor
  124. play_whine = true; // play whine sound
  125. }
  126. }
  127. }
  128. // play whine sound
  129. if (play_whine){
  130. Fvector pt;
  131. pt.mad (bullet->bullet_pos, bullet->dir, dist);
  132. Level().BulletManager().PlayWhineSound (bullet,initiator,pt);
  133. }
  134. }else{
  135. // don't test this object again (return FALSE)
  136. bRes = FALSE;
  137. }
  138.  
  139. }
  140. }
  141. }
  142.  
  143.  
  144. return bRes;
  145. }
  146.  
  147. //callback функция
  148. // result.O; // 0-static else CObject*
  149. // result.range; // range from start to element
  150. // result.element; // if (O) "num tri" else "num bone"
  151. // params; // user defined abstract data
  152. // Device.Statistic.TEST0.End();
  153. //return TRUE-продолжить трассировку / FALSE-закончить трассировку
  154.  
  155. void CBulletManager::FireShotmark (SBullet* bullet, const Fvector& vDir, const Fvector &vEnd, collide::rq_result& R, u16 target_material, const Fvector& vNormal, bool ShowMark)
  156. {
  157. SGameMtlPair* mtl_pair = GMLib.GetMaterialPair(bullet->bullet_material_idx, target_material);
  158. Fvector particle_dir = vNormal;
  159.  
  160. if (R.O)
  161. {
  162. /* add_SkeletonWallmark not implemented now...
  163. particle_dir = vDir;
  164. particle_dir.invert ();
  165.  
  166. //на текущем актере отметок не ставим
  167. if(Level().CurrentEntity() && Level().CurrentEntity()->ID() == R.O->ID()) return;
  168.  
  169. if (mtl_pair && !mtl_pair->m_pCollideMarks->empty() && ShowMark)
  170. {
  171. //добавить отметку на материале
  172. Fvector p;
  173. p.mad(bullet->bullet_pos,bullet->dir,R.range-0.01f);
  174. if(!g_dedicated_server)
  175. ::Render->add_SkeletonWallmark ( &R.O->renderable.xform,
  176. PKinematics(R.O->Visual()),
  177. &*mtl_pair->m_pCollideMarks,
  178. p,
  179. bullet->dir,
  180. bullet->wallmark_size);
  181. }
  182. */
  183. }
  184. else
  185. {
  186. //вычислить нормаль к пораженной поверхности
  187. Fvector* pVerts = Level().ObjectSpace.GetStaticVerts();
  188. CDB::TRI* pTri = Level().ObjectSpace.GetStaticTris()+R.element;
  189.  
  190. if (mtl_pair && !mtl_pair->m_pCollideMarks->empty() && ShowMark)
  191. {
  192. //добавить отметку на материале
  193. ::Render->add_StaticWallmark (&*mtl_pair->m_pCollideMarks, vEnd, bullet->wallmark_size, pTri, pVerts);
  194. }
  195. }
  196.  
  197. ref_sound* pSound = (!mtl_pair || mtl_pair->CollideSounds.empty())?
  198. NULL:&mtl_pair->CollideSounds[::Random.randI(0,mtl_pair->CollideSounds.size())];
  199.  
  200. //проиграть звук
  201. if(pSound && ShowMark)
  202. {
  203. CObject* O = Level().Objects.net_Find(bullet->parent_id );
  204. bullet->m_mtl_snd = *pSound;
  205. bullet->m_mtl_snd.play_at_pos(O, vEnd, 0);
  206. }
  207.  
  208. LPCSTR ps_name = ( !mtl_pair || mtl_pair->CollideParticles.empty() ) ? NULL :
  209. *mtl_pair->CollideParticles[ ::Random.randI(0,mtl_pair->CollideParticles.size()) ];
  210.  
  211. SGameMtl* tgt_mtl = GMLib.GetMaterialByIdx(target_material);
  212. BOOL bStatic = !tgt_mtl->Flags.test(SGameMtl::flDynamic);
  213.  
  214. if( (ps_name && ShowMark) || (bullet->flags.explosive && bStatic) )
  215. {
  216. VERIFY2 (
  217. (particle_dir.x*particle_dir.x+particle_dir.y*particle_dir.y+particle_dir.z*particle_dir.z) > flt_zero,
  218. make_string("[%f][%f][%f]", VPUSH(particle_dir))
  219. );
  220. Fmatrix pos;
  221. pos.k.normalize(particle_dir);
  222. Fvector::generate_orthonormal_basis(pos.k, pos.j, pos.i);
  223. pos.c.set(vEnd);
  224. if(ps_name && ShowMark)
  225. {
  226. //отыграть партиклы попадания в материал
  227. CParticlesObject* ps = CParticlesObject::Create(ps_name,TRUE);
  228.  
  229. ps->UpdateParent( pos, zero_vel );
  230. GamePersistent().ps_needtoplay.push_back( ps );
  231. }
  232.  
  233. if( bullet->flags.explosive && bStatic )
  234. {
  235. PlayExplodePS( pos );
  236. }
  237. }
  238. }
  239.  
  240. void CBulletManager::StaticObjectHit (CBulletManager::_event& E)
  241. {
  242. // Fvector hit_normal;
  243. FireShotmark(&E.bullet, E.bullet.dir, E.point, E.R, E.tgt_material, E.normal);
  244. // ObjectHit (&E.bullet, E.point, E.R, E.tgt_material, hit_normal);
  245. }
  246.  
  247. static bool g_clear = false;
  248. void CBulletManager::DynamicObjectHit (CBulletManager::_event& E)
  249. {
  250. //только для динамических объектов
  251. VERIFY(E.R.O);
  252.  
  253. if ( CEntity* entity = smart_cast<CEntity*>(E.R.O) )
  254. {
  255. if ( !entity->in_solid_state() )
  256. {
  257. return;
  258. }
  259. }
  260.  
  261. if (g_clear) E.Repeated = false;
  262. if (GameID() == eGameIDSingle) E.Repeated = false;
  263. bool NeedShootmark = true;//!E.Repeated;
  264.  
  265. if (smart_cast<CActor*>(E.R.O))
  266. {
  267. game_PlayerState* ps = Game().GetPlayerByGameID(E.R.O->ID());
  268. if (ps && ps->testFlag(GAME_PLAYER_FLAG_INVINCIBLE))
  269. {
  270. NeedShootmark = false;
  271. };
  272. }
  273. else if ( CBaseMonster * monster = smart_cast<CBaseMonster *>(E.R.O) )
  274. {
  275. NeedShootmark = monster->need_shotmark();
  276. }
  277.  
  278. //визуальное обозначение попадание на объекте
  279. // Fvector hit_normal;
  280. FireShotmark (&E.bullet, E.bullet.dir, E.point, E.R, E.tgt_material, E.normal, NeedShootmark);
  281.  
  282. Fvector original_dir = E.bullet.dir;
  283. //ObjectHit(&E.bullet, E.end_point, E.R, E.tgt_material, hit_normal);
  284.  
  285. SBullet_Hit hit_param = E.hit_result;
  286.  
  287. // object-space
  288. //вычислить координаты попадания
  289. Fvector p_in_object_space,position_in_bone_space;
  290. Fmatrix m_inv;
  291. m_inv.invert (E.R.O->XFORM());
  292. m_inv.transform_tiny(p_in_object_space, E.point);
  293.  
  294. // bone-space
  295. IKinematics* V = smart_cast<IKinematics*>(E.R.O->Visual());
  296.  
  297. if(V)
  298. {
  299. VERIFY3(V->LL_GetBoneVisible(u16(E.R.element)),*E.R.O->cNameVisual(),V->LL_BoneName_dbg(u16(E.R.element)));
  300. Fmatrix& m_bone = (V->LL_GetBoneInstance(u16(E.R.element))).mTransform;
  301. Fmatrix m_inv_bone;
  302. m_inv_bone.invert(m_bone);
  303. m_inv_bone.transform_tiny(position_in_bone_space, p_in_object_space);
  304. }
  305. else
  306. {
  307. position_in_bone_space.set(p_in_object_space);
  308. }
  309.  
  310. //отправить хит пораженному объекту
  311. if (E.bullet.flags.allow_sendhit && !E.Repeated)
  312. {
  313. //-------------------------------------------------
  314. bool AddStatistic = false;
  315. if (GameID() != eGameIDSingle && E.bullet.flags.allow_sendhit && smart_cast<CActor*>(E.R.O)
  316. && Game().m_WeaponUsageStatistic->CollectData())
  317. {
  318. CActor* pActor = smart_cast<CActor*>(E.R.O);
  319. if (pActor)// && pActor->g_Alive())
  320. {
  321. Game().m_WeaponUsageStatistic->OnBullet_Hit(&E.bullet, E.R.O->ID(), (s16)E.R.element, E.point);
  322. AddStatistic = true;
  323. };
  324. };
  325.  
  326. SHit Hit = SHit( hit_param.power,
  327. original_dir,
  328. NULL,
  329. u16(E.R.element),
  330. position_in_bone_space,
  331. hit_param.impulse,
  332. E.bullet.hit_type,
  333. E.bullet.armor_piercing,
  334. E.bullet.flags.aim_bullet);
  335.  
  336. Hit.GenHeader(u16((AddStatistic)? GE_HIT_STATISTIC : GE_HIT)&0xffff, E.R.O->ID());
  337. Hit.whoID = E.bullet.parent_id;
  338. Hit.weaponID = E.bullet.weapon_id;
  339. Hit.BulletID = E.bullet.m_dwID;
  340.  
  341. NET_Packet np;
  342. Hit.Write_Packet (np);
  343.  
  344. // Msg("Hit sended: %d[%d,%d]", Hit.whoID, Hit.weaponID, Hit.BulletID);
  345. CGameObject::u_EventSend(np);
  346. }
  347. }
  348.  
  349. #ifdef DEBUG
  350. FvectorVec g_hit[3];
  351. #endif
  352.  
  353. extern void random_dir (Fvector& tgt_dir, const Fvector& src_dir, float dispersion);
  354.  
  355. bool CBulletManager::ObjectHit( SBullet_Hit* hit_res, SBullet* bullet, const Fvector& end_point,
  356. collide::rq_result& R, u16 target_material, Fvector& hit_normal )
  357. {
  358. //----------- normal - start
  359. if ( R.O )
  360. {
  361. //вернуть нормаль по которой играть партиклы
  362. CCF_Skeleton* skeleton = smart_cast<CCF_Skeleton*>(R.O->CFORM());
  363. if ( skeleton )
  364. {
  365. Fvector e_center;
  366. hit_normal.set (0,0,0);
  367. if ( skeleton->_ElementCenter( (u16)R.element,e_center ) )
  368. hit_normal.sub (end_point, e_center);
  369. float len = hit_normal.square_magnitude();
  370. if ( !fis_zero(len) ) hit_normal.div (_sqrt(len));
  371. else hit_normal.invert (bullet->dir);
  372. }
  373. }
  374. else
  375. {
  376. //вычислить нормаль к поверхности
  377. Fvector* pVerts = Level().ObjectSpace.GetStaticVerts();
  378. CDB::TRI* pTri = Level().ObjectSpace.GetStaticTris()+R.element;
  379. hit_normal.mknormal (pVerts[pTri->verts[0]],pVerts[pTri->verts[1]],pVerts[pTri->verts[2]]);
  380. if ( bullet->density_mode )
  381. {
  382. Fvector new_pos;
  383. new_pos.mad(bullet->bullet_pos, bullet->dir, R.range);
  384. float l = bullet->begin_density.distance_to(new_pos);
  385. float shootFactor = l * bullet->density;
  386. bullet->speed -= shootFactor;
  387. if ( bullet->speed < 0 ) bullet->speed = 0;
  388. }
  389. if ( DOT( hit_normal, bullet->dir ) < 0 )
  390. {
  391. if ( bullet->density_mode )
  392. {
  393. // Log("WARNING: Material in material found while bullet tracing. Incorrect behaviour of shooting is possible.");
  394. }
  395. bullet->density_mode = true;
  396. SGameMtl* mtl = GMLib.GetMaterialByIdx(target_material);
  397. bullet->density = mtl->fDensityFactor;
  398. bullet->begin_density.mad( bullet->bullet_pos, bullet->dir,R.range );
  399. }
  400. else
  401. {
  402. bullet->density_mode=false;
  403. }
  404. }
  405. //----------- normal - end
  406. float old_speed = bullet->speed;
  407.  
  408. //коэффициент уменьшение силы с падением скорости
  409. float speed_factor = bullet->speed / bullet->max_speed;
  410. //получить силу хита выстрела с учетом патрона
  411. *hit_res = bullet->hit_param; //default param
  412.  
  413. hit_res->power = bullet->hit_param.power*speed_factor;
  414.  
  415. //(Если = 0, то пуля либо рикошетит(если контакт идёт по касательной), либо застряёт в текущем
  416. //объекте, если больше 0, то пуля прошивает объект)
  417.  
  418. SGameMtl* mtl = GMLib.GetMaterialByIdx( target_material );
  419. float mtl_ap = mtl->fShootFactor;
  420. float shoot_factor = 0.0f; //default >> пуля НЕ пробила материал!
  421. float ap = bullet->armor_piercing;
  422.  
  423. if ( ap > EPS && ap >= mtl_ap )
  424. {
  425. //пуля пробила материал
  426. shoot_factor = (( ap - mtl_ap ) / ap);
  427. }
  428.  
  429. hit_res->impulse = 0.0f;
  430. float speed_scale = 0.0f;
  431.  
  432. #ifdef DEBUG
  433. //Fvector dbg_bullet_pos;
  434. //dbg_bullet_pos.mad(bullet->bullet_pos,bullet->dir,R.range);
  435. int bullet_state = 0;
  436. #endif
  437.  
  438. if ( fsimilar( mtl_ap, 0.0f ) )//Если материал полностью простреливаемый (кусты)
  439. {
  440. #ifdef DEBUG
  441. bullet_state = 2;
  442. #endif
  443. return true;
  444. }
  445.  
  446. if (bullet->flags.magnetic_beam && (shoot_factor > EPS))
  447. {
  448. #ifdef DEBUG
  449. bullet_state = 2;
  450. #endif
  451. //air resistance of magnetic_beam bullet is armor resistance too
  452. bullet->armor_piercing -= mtl_ap * bullet->air_resistance;
  453. return true;
  454. }
  455.  
  456. //рикошет
  457. Fvector new_dir;
  458. new_dir.reflect ( bullet->dir,hit_normal );
  459. Fvector tgt_dir;
  460. random_dir ( tgt_dir, new_dir, deg2rad( 10.0f ) );
  461. float ricoshet_factor = bullet->dir.dotproduct( tgt_dir );
  462.  
  463. float f = Random.randF( 0.5f, 0.8f ); //(0.5f,1.f);
  464. if ( (f < ricoshet_factor) && !mtl->Flags.test(SGameMtl::flNoRicoshet) && bullet->flags.allow_ricochet )
  465. {
  466. // уменьшение скорости полета в зависимости от угла падения пули (чем прямее угол, тем больше потеря)
  467. bullet->flags.allow_ricochet = 0;
  468. float scale = 1.0f - _abs(bullet->dir.dotproduct(hit_normal)) * m_fCollisionEnergyMin;
  469. clamp(scale, 0.0f, m_fCollisionEnergyMax);
  470. speed_scale = scale;
  471.  
  472. // вычисление рикошета, делается немного фейком, т.к. пуля остается в точке столкновения
  473. // и сразу выходит из RayQuery()
  474. bullet->dir.set (tgt_dir);
  475. bullet->bullet_pos = end_point;
  476. bullet->flags.ricochet_was = 1;
  477.  
  478. #ifdef DEBUG
  479. bullet_state = 0;
  480. #endif
  481. }
  482. else if ( shoot_factor < EPS )
  483. {
  484. //застрявание пули в материале
  485. speed_scale = 0.0f;
  486. #ifdef DEBUG
  487. bullet_state = 1;
  488. #endif
  489. }
  490. else
  491. {
  492. //пробивание материала
  493. speed_scale = shoot_factor;//mtl->fShootFactor;
  494.  
  495. bullet->bullet_pos.mad(bullet->bullet_pos,bullet->dir,EPS);//fake
  496. //ввести коэффициент случайности при простреливании
  497. Fvector rand_normal;
  498. rand_normal.random_dir(bullet->dir, deg2rad(2.0f), Random);
  499. bullet->dir.set(rand_normal);
  500. #ifdef DEBUG
  501. bullet_state = 2;
  502. #endif
  503. }
  504.  
  505. //уменьшить скорость в зависимости от простреливаемости
  506. bullet->speed *= speed_scale;
  507. //сколько энергии в процентах потеряла пуля при столкновении
  508. float energy_lost = 1.0f - bullet->speed / old_speed;
  509. //импульс переданный объекту равен прямопропорционален потерянной энергии
  510. hit_res->impulse = bullet->hit_param.impulse * speed_factor * energy_lost;
  511.  
  512.  
  513. #ifdef DEBUG
  514. extern BOOL g_bDrawBulletHit;
  515. if(g_bDrawBulletHit)
  516. {
  517. // g_hit[bullet_state].push_back(dbg_bullet_pos);
  518. g_hit[bullet_state].push_back(end_point);
  519. }
  520. #endif
  521.  
  522. return true;
  523. }
Add Comment
Please, Sign In to add comment