dayzplayerimplementmeleecombat.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  1. enum EMeleeHitType
  2. {
  3. NONE = -1,
  4. LIGHT,
  5. HEAVY,
  6. SPRINT,
  7. KICK,
  8. FINISHER_LIVERSTAB,
  9. FINISHER_NECKSTAB,
  10. FINISHER_GENERIC,
  11. WPN_HIT,
  12. WPN_HIT_BUTTSTOCK,
  13. WPN_STAB,
  14. WPN_STAB_FINISHER,
  15. }
  16. class DayZPlayerImplementMeleeCombat
  17. {
  18. //! Target selection settings
  19. protected const float TARGETING_ANGLE_NORMAL = 30.0; //!< Second Pass: Half angle of cone
  20. protected const float TARGETING_ANGLE_SPRINT = 15.0; //!< Second Pass: Half angle of cone during sprint
  21. protected const float TARGETING_MIN_HEIGHT = -2.0; //!< Second Pass: How deep the cone goes in meters from player position
  22. protected const float TARGETING_MAX_HEIGHT = 2.0; //!< Second Pass: How high the cone goes in meters from player position
  23. protected const float TARGETING_RAY_RADIUS_EX = 0.5; //!< Second Pass: Max distance from ray projected from player looking direction
  24. protected const float TARGETING_RAY_RADIUS = 0.25; //!< DEPRECATED: "HitZoneSelection"
  25. protected const float TARGETING_RAY_DIST = 5.0; //!< DEPRECATED: "HitZoneSelection"
  26. protected const float TARGETING_RAY_DIST_SHORT= 2.0; //!< DEPRECATED: "HitZoneSelection"
  27. protected const float RANGE_EXTENDER_NORMAL = 0.65; //!< General range extension
  28. protected const float RANGE_EXTENDER_SPRINT = 1.35; //!< General range extension while in sprint
  29. protected const string DEFAULT_HIT_ZONE = "Torso"; //!< DEPRECATED: "HitZoneSelection"
  30. //! Target selecting "component"
  31. protected ref MeleeTargeting m_MeleeTargeting; //!< Contains logic for Second Pass
  32. //! Targets - types
  33. protected Object m_TargetObject; //!< Main target found during most recent TargetSelection
  34. protected EMeleeTargetType m_TargetType; //!< DEPRECATED: Was added but never used..?
  35. protected ref array<Object> m_AllTargetObjects; //!< All potential targets found during most recent TargetSelection
  36. #ifdef DIAG_DEVELOPER
  37. protected Object m_PreviousTargetObject; //!< Main target found during most recent TargetSelection
  38. protected ref array<Object> m_AllPreviousTargetObjects; //!< All potential targets found during most recent TargetSelection
  39. #endif
  40. protected ref array<typename> m_TargetableObjects; //!< Typenames of all directly/preferred targetable objects (1st Pass + 2nd Pass)
  41. protected ref array<typename> m_NonAlignableObjects; //!< Typenames of objects that can be targeted, but are not a priority (3rd Pass)
  42. protected ref array<string> m_BlacklistedDamageZones; //!< List of blacklisted damage zone names (cannot use indices due to the possible changes when p3d components are recalculated)
  43. //! Parent
  44. protected DayZPlayerImplement m_DZPlayer; //!< Player executing the melee
  45. //! Weapons - cache
  46. protected InventoryItem m_Weapon; //!< Weapon used during most recent Update
  47. protected int m_WeaponMode; //!< WeaponMode used during most recent Update
  48. protected float m_WeaponRange; //!< WeaponRange used during most recent Update
  49. //! Misc - cache
  50. protected bool m_ForceUntargetable; //!< Forcing current target as untargetable
  51. protected bool m_SprintAttack; //!< If most recent attack was a sprint attack
  52. protected bool m_WasHit; //!< If most recent attack was
  53. protected vector m_RayStart; //!< Start position of most recent HitZoneSelectionRaycast
  54. protected vector m_RayEnd; //!< End position of most recent HitZoneSelectionRaycast
  55. protected vector m_RayEndShort; //!< DEPRECATED: "HitZoneSelection"
  56. protected EMeleeHitType m_HitType; //!< Hit type of the most recent attack
  57. //! Hit result - cache
  58. protected int m_HitZoneIdx; //!< Most recent target HitZone index
  59. protected int m_FinisherType;
  60. protected string m_HitZoneName; //!< Most recent target HitZone name
  61. protected vector m_HitPositionWS; //!< Most recent target position
  62. #ifdef DIAG_DEVELOPER
  63. protected int m_PreviousHitZoneIdx; //!< Most recent target HitZone index
  64. protected string m_PreviousHitZoneName; //!< Most recent target HitZone name
  65. protected vector m_PreviousHitPositionWS; //!< Most recent target position
  66. #endif
  67. private int m_DebugForcedFinisherType;
  68. // ------------------------------------------------------------
  69. // CONSTRUCTOR
  70. // ------------------------------------------------------------
  71. void DayZPlayerImplementMeleeCombat(DayZPlayerImplement player)
  72. {
  73. Init(player);
  74. }
  75. void Init(DayZPlayerImplement player)
  76. {
  77. m_DZPlayer = player;
  78. m_MeleeTargeting = new MeleeTargeting;
  79. m_HitZoneName = "";
  80. m_HitZoneIdx = -1;
  81. m_FinisherType = -1;
  82. m_HitPositionWS = vector.Zero;
  83. m_SprintAttack = false;
  84. m_WasHit = false;
  85. m_TargetObject = null;
  86. m_TargetType = EMeleeTargetType.ALIGNABLE;
  87. m_AllTargetObjects = new array<Object>;
  88. #ifdef DIAG_DEVELOPER
  89. m_AllPreviousTargetObjects = new array<Object>;
  90. #endif
  91. m_DebugForcedFinisherType = -1;
  92. m_TargetableObjects = new array<typename>; //checks against CONFIG hierarchy
  93. m_TargetableObjects.Insert(DayZPlayer);
  94. m_TargetableObjects.Insert(DayZInfected);
  95. m_TargetableObjects.Insert(DayZAnimal);
  96. m_NonAlignableObjects = new array<typename>;
  97. m_NonAlignableObjects.Insert(Building);
  98. m_NonAlignableObjects.Insert(Transport);
  99. m_NonAlignableObjects.Insert(CarWheel);
  100. m_NonAlignableObjects.Insert(CarDoor);
  101. m_NonAlignableObjects.Insert(TentBase);
  102. m_NonAlignableObjects.Insert(BaseBuildingBase);
  103. m_BlacklistedDamageZones = new array<string>;
  104. m_BlacklistedDamageZones.Insert("Brain");
  105. }
  106. void ~DayZPlayerImplementMeleeCombat() {}
  107. // ------------------------------------------------------------
  108. // PUBLIC
  109. // ------------------------------------------------------------
  110. EMeleeHitType GetHitType()
  111. {
  112. return m_HitType;
  113. }
  114. void SetHitZoneIdx(int pHitZone)
  115. {
  116. m_HitZoneIdx = pHitZone;
  117. }
  118. EntityAI GetTargetEntity()
  119. {
  120. return EntityAI.Cast(m_TargetObject);
  121. }
  122. void SetTargetObject(Object pTarget)
  123. {
  124. m_TargetObject = pTarget;
  125. }
  126. //! component idx
  127. int GetHitZoneIdx()
  128. {
  129. return m_HitZoneIdx;
  130. }
  131. vector GetHitPos()
  132. {
  133. return m_HitPositionWS;
  134. }
  135. void SetHitPos(vector pHitPos)
  136. {
  137. m_HitPositionWS = pHitPos;
  138. }
  139. int GetFinisherType()
  140. {
  141. return m_FinisherType;
  142. }
  143. void SetFinisherType(int pFinisherType)
  144. {
  145. m_FinisherType = pFinisherType;
  146. }
  147. int GetWeaponMode()
  148. {
  149. return m_WeaponMode;
  150. }
  151. void Reset(InventoryItem weapon, EMeleeHitType hitMask, bool wasHitEvent = false)
  152. {
  153. m_Weapon = weapon;
  154. m_HitType = hitMask;
  155. m_TargetType = EMeleeTargetType.ALIGNABLE;
  156. m_SprintAttack = hitMask == EMeleeHitType.SPRINT;
  157. m_WeaponMode = SelectWeaponMode(weapon);
  158. m_WeaponRange = GetWeaponRange(weapon, m_WeaponMode);
  159. m_WasHit = wasHitEvent;
  160. #ifdef DIAG_DEVELOPER
  161. m_AllPreviousTargetObjects = m_AllTargetObjects;
  162. #endif
  163. m_AllTargetObjects.Clear();
  164. }
  165. void ResetTarget()
  166. {
  167. #ifdef DIAG_DEVELOPER
  168. m_PreviousTargetObject = m_TargetObject;
  169. m_PreviousHitPositionWS = m_HitPositionWS;
  170. m_PreviousHitZoneIdx = m_HitZoneIdx;
  171. m_PreviousHitZoneName = m_HitZoneName;
  172. #endif
  173. InternalResetTarget();
  174. }
  175. void Update(InventoryItem weapon, EMeleeHitType hitMask, bool wasHitEvent = false)
  176. {
  177. Reset(weapon, hitMask, wasHitEvent);
  178. #ifndef SERVER
  179. if (!ScriptInputUserData.CanStoreInputUserData())
  180. {
  181. //Error("DayZPlayerImplementMeleeCombat - ScriptInputUserData already posted");
  182. return;
  183. }
  184. TargetSelection();
  185. SetFinisherType(TrySelectFinisherType(weapon, GetTargetEntity()));
  186. //! Store target into input packet
  187. if (GetGame().IsMultiplayer())
  188. {
  189. ScriptInputUserData ctx = new ScriptInputUserData();
  190. ctx.Write(INPUT_UDT_MELEE_TARGET);
  191. ctx.Write(m_TargetObject);
  192. ctx.Write(m_HitPositionWS);
  193. ctx.Write(m_HitZoneIdx);
  194. ctx.Write(m_FinisherType);
  195. ctx.Send();
  196. }
  197. #endif
  198. }
  199. void CheckMeleeItem()
  200. {
  201. if (m_Weapon)
  202. {
  203. ItemBase item = ItemBase.Cast(m_Weapon.ProcessMeleeItemDamage(GetWeaponMode()));
  204. if (item && item.GetHierarchyRootPlayer())
  205. {
  206. PlayerBase.Cast(item.GetHierarchyRootPlayer()).SetCheckMeleeItem(item);
  207. }
  208. else if (m_Weapon && m_Weapon.GetHierarchyRootPlayer())
  209. {
  210. PlayerBase.Cast(m_Weapon.GetHierarchyRootPlayer()).SetCheckMeleeItem(ItemBase.Cast(m_Weapon));
  211. }
  212. }
  213. }
  214. // ------------------------------------------------------------
  215. // protected
  216. // ------------------------------------------------------------
  217. protected int SelectWeaponMode(InventoryItem weapon)
  218. {
  219. if (weapon)
  220. {
  221. //! melee with firearm
  222. if (weapon.IsInherited(Weapon))
  223. {
  224. switch (m_HitType)
  225. {
  226. case EMeleeHitType.WPN_HIT:
  227. return 0;
  228. case EMeleeHitType.WPN_HIT_BUTTSTOCK:
  229. return 1;
  230. case EMeleeHitType.WPN_STAB:
  231. return 2;
  232. }
  233. }
  234. else
  235. {
  236. //! melee weapon attacks - gets mode from the item
  237. switch (m_HitType)
  238. {
  239. case EMeleeHitType.LIGHT:
  240. return weapon.GetMeleeMode();
  241. case EMeleeHitType.HEAVY:
  242. return weapon.GetMeleeHeavyMode();
  243. case EMeleeHitType.SPRINT:
  244. return weapon.GetMeleeSprintMode();
  245. }
  246. }
  247. }
  248. //! bare hand melee mode selection
  249. switch (m_HitType)
  250. {
  251. case EMeleeHitType.HEAVY:
  252. return 1;
  253. case EMeleeHitType.SPRINT:
  254. return 2;
  255. }
  256. return 0; //! default bare-hand light attack
  257. }
  258. protected float GetWeaponRange(InventoryItem weapon, int weaponMode)
  259. {
  260. if ( weapon )
  261. return weapon.GetMeleeCombatData().GetModeRange(weaponMode);
  262. else
  263. return m_DZPlayer.GetMeleeCombatData().GetModeRange(weaponMode);
  264. }
  265. protected float GetRange()
  266. {
  267. return m_WeaponRange + RANGE_EXTENDER_NORMAL;
  268. }
  269. protected float GetAngle()
  270. {
  271. if (m_SprintAttack)
  272. return TARGETING_ANGLE_SPRINT;
  273. else
  274. return TARGETING_ANGLE_NORMAL;
  275. }
  276. protected void TargetSelection()
  277. {
  278. // Prepare common variables
  279. PlayerBase player = PlayerBase.Cast(m_DZPlayer);
  280. vector pos = m_DZPlayer.GetPosition();
  281. vector rayStart = m_DZPlayer.GetBonePositionWS(m_DZPlayer.GetBoneIndexByName("Head"));
  282. vector cameraPos, cameraRot, cameraDir;
  283. m_DZPlayer.GetCurrentCameraTransform(cameraPos, cameraDir, cameraRot);
  284. vector dir = MiscGameplayFunctions.GetHeadingVector(player);
  285. dir[1] = cameraDir[1];
  286. // Calculate max distances
  287. float dist = GetRange();
  288. float angle = GetAngle();
  289. float dist2 = Math.SqrFloat(dist);
  290. // There's generally 2 TargetSelection calls per animation
  291. // First to obtain target to move towards during animation
  292. // Second one is to see if that target is still in range before applying damage to it
  293. // m_WasHit means the hit event occured, so this is the second call
  294. if (m_WasHit && GetFinisherType() == -1)
  295. {
  296. // See if the component is still in range
  297. if (CanObjectBeTargeted(m_TargetObject) && ( vector.DistanceSq(rayStart, m_TargetObject.GetDamageZonePos(m_HitZoneName)) <= dist2 ))
  298. {
  299. m_AllTargetObjects.Insert(m_TargetObject);
  300. // This result should still be cached, so no need to fill it in again
  301. return;
  302. }
  303. }
  304. // Find a new target
  305. InternalResetTarget();
  306. Object target;
  307. vector hitPos;
  308. int hitZone;
  309. // First pass - Prefer what the player is looking at (only aligneable ones, we will try for nonaligneable later)
  310. if (HitZoneSelectionRaycastHelper(hitPos, hitZone, target))
  311. {
  312. if (m_ForceUntargetable)
  313. {
  314. SetTarget(target, hitPos, hitZone);
  315. return;
  316. }
  317. if (CanObjectBeTargeted(target) && vector.DistanceSq(rayStart, hitPos) <= dist2)
  318. {
  319. m_AllTargetObjects.Insert(target);
  320. SetTarget(target, hitPos, hitZone);
  321. return;
  322. }
  323. }
  324. // Second pass - Try to obtain something aligneable closeby the player watching direction
  325. MeleeTargetData targetData = m_MeleeTargeting.GetMeleeTargetEx(new MeleeTargetSettings(pos, dist*0.75, angle, TARGETING_MIN_HEIGHT, TARGETING_MAX_HEIGHT, rayStart, dir, TARGETING_RAY_RADIUS_EX, m_DZPlayer, m_TargetableObjects), m_AllTargetObjects, m_BlacklistedDamageZones);
  326. if (targetData)
  327. {
  328. SetTarget(targetData.Obj, targetData.HitPos, targetData.HitComponent);
  329. return;
  330. }
  331. // Third pass - We found no aligneable target, check if the target that we are directly looking at is nonalignable (big)
  332. if (CanObjectBeTargeted(target, true) && vector.DistanceSq(rayStart, hitPos) <= dist2)
  333. {
  334. m_AllTargetObjects.Insert(target);
  335. SetTarget(target, hitPos, hitZone);
  336. return;
  337. }
  338. }
  339. /**
  340. \brief General condition for finisher attacks
  341. \param weapon \p Weapon used in the attack
  342. \param target \p Target entity
  343. \return \p int - type of finisher (-1 == no finisher)
  344. */
  345. protected int TrySelectFinisherType(InventoryItem weapon, EntityAI target)
  346. {
  347. if (m_WasHit)
  348. return -1;
  349. if (target)
  350. {
  351. vector dir = target.GetPosition() - m_DZPlayer.GetPosition();
  352. IEntity hitEntity;
  353. vector hitPos, hitNormal;
  354. float moveFraction = m_DZPlayer.CollisionMoveTest(dir, vector.Zero, 1.0, target, hitEntity, hitPos, hitNormal);
  355. if (moveFraction < 1.0)
  356. return -1;
  357. }
  358. //! perform only for finisher suitable weapons
  359. if (target && target.CanBeBackstabbed() && weapon && (weapon.IsMeleeFinisher() || m_HitType == EMeleeHitType.WPN_STAB) && !weapon.IsRuined() )
  360. {
  361. bool playGenericFinisherAnimation = false;
  362. ZombieBase targetZombie = ZombieBase.Cast(target);
  363. if (targetZombie && m_DebugForcedFinisherType == -1)
  364. {
  365. //! check if attacker is in right pos and angle against victim
  366. if (!IsEntityBehindEntityInAngle(m_DZPlayer, target, 60))
  367. {
  368. return -1;
  369. }
  370. int mindState = targetZombie.GetMindStateSynced();
  371. //! Check if the infected is aware of the player
  372. if (mindState >= DayZInfectedConstants.MINDSTATE_DISTURBED)
  373. {
  374. return -1;
  375. }
  376. }
  377. PlayerBase targetPlayer = PlayerBase.Cast(target);
  378. //! prone checks
  379. if (targetZombie)
  380. {
  381. playGenericFinisherAnimation = targetZombie.IsCrawling();
  382. }
  383. else if (targetPlayer)
  384. {
  385. playGenericFinisherAnimation = targetPlayer.IsInProne();
  386. }
  387. else
  388. {
  389. playGenericFinisherAnimation = true;
  390. }
  391. //! firearm
  392. if (weapon.IsWeapon())
  393. {
  394. return EMeleeHitType.WPN_STAB_FINISHER;
  395. }
  396. else if (playGenericFinisherAnimation)
  397. {
  398. return EMeleeHitType.FINISHER_GENERIC;
  399. }
  400. else //specific hit depending on the component hit (gotten from the target)
  401. {
  402. return DetermineSpecificFinisherType(ItemBase.Cast(weapon));
  403. }
  404. }
  405. return -1;
  406. }
  407. protected int DetermineSpecificFinisherType(ItemBase weapon)
  408. {
  409. if (m_DebugForcedFinisherType > -1)
  410. {
  411. array<int> finishers = {
  412. EMeleeHitType.FINISHER_LIVERSTAB,
  413. EMeleeHitType.FINISHER_NECKSTAB
  414. };
  415. return finishers[m_DebugForcedFinisherType];
  416. }
  417. if (!weapon || !weapon.GetValidFinishers() || weapon.GetValidFinishers().Count() == 0)
  418. {
  419. return EMeleeHitType.FINISHER_LIVERSTAB;
  420. }
  421. PlayerBase player = PlayerBase.Cast(m_DZPlayer);
  422. int idx = Math.Round(Math.Lerp(0, weapon.GetValidFinishers().Count() - 1, player.GetRandomGeneratorSyncManager().GetRandom01(RandomGeneratorSyncUsage.RGSGeneric)));
  423. return weapon.GetValidFinishers()[idx];
  424. }
  425. protected void InternalResetTarget()
  426. {
  427. m_TargetObject = null;
  428. m_HitPositionWS = vector.Zero;
  429. m_HitZoneIdx = -1;
  430. m_HitZoneName = "";
  431. SetFinisherType(-1);
  432. }
  433. protected void SetTarget(Object obj, vector hitPos, int hitZone)
  434. {
  435. if (obj)
  436. {
  437. m_TargetObject = obj;
  438. m_HitPositionWS = hitPos;
  439. m_HitZoneIdx = hitZone;
  440. m_HitZoneName = m_TargetObject.GetDamageZoneNameByComponentIndex(m_HitZoneIdx);
  441. }
  442. }
  443. protected bool CanObjectBeTargeted(Object obj, bool checkNonAligneAble = false)
  444. {
  445. return obj && obj.IsAlive() && ( obj.IsAnyInherited(m_TargetableObjects) || (checkNonAligneAble && obj.IsAnyInherited(m_NonAlignableObjects)) );
  446. }
  447. protected bool HitZoneSelectionRaycastHelper(out vector hitPos, out int hitZone, out Object target)
  448. {
  449. return HitZoneSelectionRaycast(hitPos, hitZone, target, false);
  450. // Not sure if desired, as it can give some results that feel a little odd
  451. // But it will depend entirely on the player and situation..
  452. // I guess the crosshair is hidden while melee anyways though
  453. // || ( m_DZPlayer.IsInThirdPerson() && HitZoneSelectionRaycast(hitPos, hitZone, target, true);
  454. }
  455. protected bool HitZoneSelectionRaycast(out vector hitPos, out int hitZone, out Object target, bool useCamera)
  456. {
  457. PlayerBase player = PlayerBase.Cast(m_DZPlayer);
  458. vector pos;
  459. vector dir;
  460. vector playerDir;
  461. if (useCamera)
  462. { // What the player camera is looking at (crosshair)
  463. vector cameraRotation;
  464. player.GetCurrentCameraTransform(pos, dir, cameraRotation);
  465. playerDir = dir;
  466. }
  467. else
  468. { // What the player himself is looking at
  469. playerDir = MiscGameplayFunctions.GetHeadingVector(player);
  470. dir = GetGame().GetCurrentCameraDirection();
  471. MiscGameplayFunctions.GetHeadBonePos(player, pos);
  472. }
  473. //! Prevents targeting of objects behind player
  474. if (vector.Dot(dir, playerDir) < 0.5)
  475. {
  476. return false;
  477. }
  478. m_RayStart = pos;
  479. m_RayEnd = m_RayStart + GetRange() * dir;
  480. // raycast
  481. set<Object> hitObjects = new set<Object>;
  482. vector hitNormal;
  483. if ( DayZPhysics.RaycastRV(m_RayStart, m_RayEnd, hitPos, hitNormal, hitZone, hitObjects, null, player, false, false, ObjIntersectIFire) && hitObjects.Count() > 0 )
  484. {
  485. target = hitObjects[0];
  486. m_ForceUntargetable = false;
  487. //! Opponent is inside car - targeting range is shorter in that case
  488. PlayerBase playerTarget = PlayerBase.Cast(target);
  489. if (playerTarget && playerTarget.IsInVehicle())
  490. {
  491. if (vector.DistanceSq(pos, hitPos) > Math.SqrFloat(GetRange() * 0.5))
  492. {
  493. m_ForceUntargetable = true;
  494. target = null;
  495. hitPos = vector.Zero;
  496. hitZone = -1;
  497. }
  498. }
  499. return true;
  500. }
  501. return false;
  502. }
  503. //! DEPRECATED
  504. protected void HitZoneSelection()
  505. {
  506. Object cursorTarget = null;
  507. PlayerBase player = PlayerBase.Cast(m_DZPlayer);
  508. // ray properties
  509. vector pos;
  510. vector cameraDirection = GetGame().GetCurrentCameraDirection();
  511. MiscGameplayFunctions.GetHeadBonePos(player, pos);
  512. m_RayStart = pos;
  513. m_RayEnd = pos + cameraDirection * TARGETING_RAY_DIST;
  514. m_RayEndShort = pos + cameraDirection * TARGETING_RAY_DIST_SHORT;
  515. // raycast
  516. set<Object> hitObjects = new set<Object>;
  517. int hitComponentIndex;
  518. float hitFraction;
  519. vector start, end, hitNormal, hitPosObstructed;
  520. PhxInteractionLayers collisionLayerMask = PhxInteractionLayers.DEFAULT;
  521. if ( !DayZPhysics.RaycastRV(m_RayStart, m_RayEndShort, m_HitPositionWS, hitNormal, m_HitZoneIdx, hitObjects, null, player, false, false, ObjIntersectIFire) && !DayZPhysics.RaycastRV(m_RayStart, m_RayEnd, m_HitPositionWS, hitNormal, m_HitZoneIdx, hitObjects, null, player, false, false, ObjIntersectIFire, TARGETING_RAY_RADIUS) )
  522. {
  523. m_HitZoneIdx = -1;
  524. //Print("HitZoneSelection failed");
  525. }
  526. else if ( hitObjects.Count() > 0 )
  527. {
  528. cursorTarget = hitObjects.Get(0);
  529. //! make sure we are in range of the current weapon;
  530. vector playerPos = m_DZPlayer.GetPosition();
  531. vector hitPos = m_HitPositionWS;
  532. //! 2d only
  533. playerPos[1] = 0;
  534. hitPos[1] = 0;
  535. //! just for building and transports (big objects)
  536. if ( cursorTarget.IsAnyInherited(m_NonAlignableObjects) && vector.Distance(playerPos, hitPos) <= GetWeaponRange(m_Weapon, GetWeaponMode()))
  537. {
  538. //! if no object in cone, set this object from raycast for these special cases
  539. if (m_TargetObject == null)
  540. {
  541. m_TargetObject = cursorTarget;
  542. }
  543. }
  544. if (cursorTarget == m_TargetObject)
  545. {
  546. m_HitZoneName = cursorTarget.GetDamageZoneNameByComponentIndex(m_HitZoneIdx);
  547. //Print("hit object: " + m_TargetObject + " | component idx: " + m_HitZoneIdx + " | hitzone name: " + m_HitZoneName);
  548. }
  549. else
  550. {
  551. if (m_TargetObject == DayZInfected.Cast(m_TargetObject) || m_TargetObject == PlayerBase.Cast(m_TargetObject))
  552. m_HitZoneName = DEFAULT_HIT_ZONE; //Default to torso if no zone is targeted
  553. }
  554. }
  555. else
  556. {
  557. m_HitZoneIdx = -1;
  558. //Print("HitZoneSelection failed");
  559. }
  560. }
  561. //! DEPRECATED - New function in MeleeTargeting
  562. protected bool IsObstructed(Object object)
  563. {
  564. // check direct visibility of object (obstruction check)
  565. PhxInteractionLayers collisionLayerMask = PhxInteractionLayers.BUILDING|PhxInteractionLayers.DOOR|PhxInteractionLayers.VEHICLE|PhxInteractionLayers.ROADWAY|PhxInteractionLayers.TERRAIN|PhxInteractionLayers.ITEM_SMALL|PhxInteractionLayers.ITEM_LARGE|PhxInteractionLayers.FENCE;
  566. int hitComponentIndex;
  567. float hitFraction;
  568. vector start, end, hitNormal, hitPosObstructed;
  569. Object hitObject = null;
  570. PlayerBase player = PlayerBase.Cast(m_DZPlayer);
  571. if (object)
  572. {
  573. MiscGameplayFunctions.GetHeadBonePos(player, start);
  574. end = start + MiscGameplayFunctions.GetHeadingVector(player) * vector.Distance(player.GetPosition(), object.GetPosition());
  575. if ( end == start )
  576. return true; //! not possible to trace when this happens (zero length raycast)
  577. return DayZPhysics.RayCastBullet( start, end, collisionLayerMask, null, hitObject, hitPosObstructed, hitNormal, hitFraction);
  578. }
  579. return false;
  580. }
  581. private bool IsEntityBehindEntityInAngle(EntityAI source, EntityAI target, float angle)
  582. {
  583. vector targetDirection = target.GetDirection();
  584. ZombieBase targetZombie;
  585. if (Class.CastTo(targetZombie, target))
  586. {
  587. targetDirection = Vector(targetZombie.GetOrientationSynced(),0,0);
  588. targetDirection = targetDirection.AnglesToVector();
  589. }
  590. vector toSourceDirection = (source.GetPosition() - target.GetPosition());
  591. targetDirection[1] = 0;
  592. toSourceDirection[1] = 0;
  593. targetDirection.Normalize();
  594. toSourceDirection.Normalize();
  595. float cosFi = vector.Dot(targetDirection, toSourceDirection);
  596. vector cross = targetDirection * toSourceDirection;
  597. int hitDir = Math.Acos(cosFi) * Math.RAD2DEG;
  598. if (cross[1] < 0)
  599. hitDir = -hitDir;
  600. return hitDir <= (-180 + angle) || hitDir >= (180 - angle);
  601. }
  602. #ifdef DIAG_DEVELOPER
  603. // ------------------------------------------------------------
  604. // DEBUG
  605. // ------------------------------------------------------------
  606. protected ref array<Shape> dbgConeShapes = new array<Shape>();
  607. protected ref array<Shape> dbgTargets = new array<Shape>();
  608. protected ref array<Shape> hitPosShapes = new array<Shape>();
  609. void Debug(InventoryItem weapon, EMeleeHitType hitType)
  610. {
  611. CleanAllDebugShapes();
  612. if (!DiagMenu.GetBool(DiagMenuIDs.MELEE_DEBUG))
  613. return;
  614. if (DiagMenu.GetBool(DiagMenuIDs.MELEE_CONTINUOUS) && (!GetGame().IsMultiplayer() || !GetGame().IsServer()))
  615. Update(weapon, hitType);
  616. if (DiagMenu.GetBool(DiagMenuIDs.MELEE_SHOW_TARGETS))
  617. ShowDebugMeleeTarget();
  618. if (DiagMenu.GetBool(DiagMenuIDs.MELEE_DRAW_TARGETS))
  619. DrawDebugTargets();
  620. if (DiagMenu.GetBool(DiagMenuIDs.MELEE_DRAW_RANGE))
  621. {
  622. DrawDebugMeleeHitPosition();
  623. DrawDebugMeleeCone();
  624. }
  625. if (DiagMenu.GetBool(DiagMenuIDs.MELEE_DRAW_BLOCK_RANGE_AI))
  626. DrawDebugBlockCone(GameConstants.AI_MAX_BLOCKABLE_ANGLE, COLOR_GREEN);
  627. if (DiagMenu.GetBool(DiagMenuIDs.MELEE_DRAW_BLOCK_RANGE_PVP))
  628. DrawDebugBlockCone(GameConstants.PVP_MAX_BLOCKABLE_ANGLE, COLOR_YELLOW);
  629. }
  630. int DebugGetForcedFinisherType()
  631. {
  632. return m_DebugForcedFinisherType;
  633. }
  634. void DebugSetForcedFinisherType(int pFinisherType)
  635. {
  636. m_DebugForcedFinisherType = pFinisherType;
  637. }
  638. //! shows target in DbgUI 'window'
  639. protected void ShowDebugMeleeTarget()
  640. {
  641. int windowPosX = 0;
  642. int windowPosY = 500;
  643. DbgUI.Begin("Melee Target", windowPosX, windowPosY);
  644. HumanCommandMelee2 hmc2 = m_DZPlayer.GetCommand_Melee2();
  645. if (hmc2)
  646. {
  647. DbgUI.Text("Current combo: " + hmc2.GetComboCount());
  648. }
  649. if (m_PreviousTargetObject)
  650. {
  651. DbgUI.Text("Previous Character: " + m_PreviousTargetObject.GetDisplayName());
  652. DbgUI.Text("Previous HitZone: " + m_PreviousHitZoneName + "(" + m_PreviousHitZoneIdx + ")");
  653. DbgUI.Text("Previous HitPosWS:" + m_PreviousHitPositionWS);
  654. DbgUI.Text("Previous Distance:" + vector.Distance(m_PreviousHitPositionWS, m_DZPlayer.GetPosition()));
  655. }
  656. if (m_TargetObject)
  657. {
  658. DbgUI.Text("Character: " + m_TargetObject.GetDisplayName());
  659. DbgUI.Text("HitZone: " + m_HitZoneName + "(" + m_HitZoneIdx + ")");
  660. DbgUI.Text("HitPosWS:" + m_HitPositionWS);
  661. DbgUI.Text("Distance:" + vector.Distance(m_HitPositionWS, m_DZPlayer.GetPosition()));
  662. }
  663. DbgUI.End();
  664. }
  665. //! shows debug sphere above the target
  666. protected void DrawDebugTargets()
  667. {
  668. DrawDebugTargetsHelper(m_AllPreviousTargetObjects, m_PreviousTargetObject, COLOR_RED_A, COLOR_YELLOW_A);
  669. DrawDebugTargetsHelper(m_AllTargetObjects, m_TargetObject, COLOR_RED, COLOR_YELLOW);
  670. }
  671. protected void DrawDebugTargetsHelper(array<Object> allTargets, Object target, int colorMainTarget, int colorTarget)
  672. {
  673. for (int i = 0; i < allTargets.Count(); ++i )
  674. {
  675. if ( m_TargetObject && allTargets.Count() )
  676. {
  677. Object obj = allTargets[i];
  678. vector w_pos = obj.GetPosition();
  679. // sphere pos tweaks
  680. vector w_pos_sphr = w_pos;
  681. w_pos_sphr[1] = w_pos_sphr[1] + 1.8;
  682. // line pos tweaks
  683. vector w_pos_lend = w_pos;
  684. w_pos_lend[1] = w_pos_lend[1] + 1.8;
  685. if ( obj == m_TargetObject )
  686. {
  687. dbgTargets.Insert( Debug.DrawSphere(w_pos_sphr, 0.05, colorMainTarget, ShapeFlags.NOOUTLINE) );
  688. dbgTargets.Insert( Debug.DrawLine(w_pos, w_pos_lend, colorMainTarget) );
  689. }
  690. else
  691. {
  692. dbgTargets.Insert( Debug.DrawSphere(w_pos_sphr, 0.05, colorTarget, ShapeFlags.NOOUTLINE) );
  693. dbgTargets.Insert( Debug.DrawLine(w_pos, w_pos_lend, colorTarget) );
  694. }
  695. }
  696. }
  697. }
  698. protected void DrawDebugMeleeHitPosition()
  699. {
  700. if (m_PreviousTargetObject)
  701. hitPosShapes.Insert( Debug.DrawSphere(m_PreviousHitPositionWS, 0.15, COLOR_YELLOW_A, ShapeFlags.NOOUTLINE|ShapeFlags.TRANSP) );
  702. if (m_TargetObject)
  703. hitPosShapes.Insert( Debug.DrawSphere(m_HitPositionWS, 0.15, COLOR_YELLOW, ShapeFlags.NOOUTLINE|ShapeFlags.TRANSP) );
  704. }
  705. protected void DrawDebugMeleeCone()
  706. {
  707. // cone settings
  708. float dist = GetRange();
  709. vector start = m_DZPlayer.GetPosition();
  710. vector normDir = MiscGameplayFunctions.GetHeadingVector(PlayerBase.Cast(m_DZPlayer));
  711. normDir[1] = 0;
  712. normDir.Normalize();
  713. float playerAngle = -Math.Atan2(normDir[0], normDir[2]);
  714. dbgConeShapes.InsertArray(Debug.DrawCone(start, dist, GetAngle() * Math.DEG2RAD, playerAngle + Math.PI_HALF, COLOR_BLUE));
  715. }
  716. protected void DrawDebugBlockCone(float angle, int color)
  717. {
  718. // cone settings
  719. float dist = 3;
  720. vector start = m_DZPlayer.GetPosition();
  721. vector dir = GetGame().GetCurrentCameraDirection();
  722. dir[1] = 0;
  723. dir.Normalize();
  724. float playerAngle = -Math.Atan2(dir[0], dir[2]);
  725. dbgConeShapes.InsertArray(Debug.DrawCone(start, dist, angle * Math.DEG2RAD, playerAngle + Math.PI_HALF, color));
  726. }
  727. protected void CleanAllDebugShapes()
  728. {
  729. CleanupDebugShapes(dbgTargets);
  730. CleanupDebugShapes(dbgConeShapes);
  731. CleanupDebugShapes(hitPosShapes);
  732. }
  733. protected void CleanupDebugShapes(array<Shape> shapes)
  734. {
  735. for ( int it = 0; it < shapes.Count(); ++it )
  736. Debug.RemoveShape( shapes[it] );
  737. shapes.Clear();
  738. }
  739. #endif
  740. }