dayzplayermeleefightlogic_lightheavy.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. //! Light / Heavy punches
  2. enum EFightLogicCooldownCategory
  3. {
  4. EVADE = 0
  5. }
  6. class DayZPlayerMeleeFightLogic_LightHeavy
  7. {
  8. //! cooldown timers settings
  9. const float EVADE_COOLDOWN = 0.5;
  10. //
  11. const float CLOSE_TARGET_DISTANCE = 1.5;
  12. //! dummy ammo types
  13. const string DUMMY_LIGHT_AMMO = "Dummy_Light";
  14. const string DUMMY_HEAVY_AMMO = "Dummy_Heavy";
  15. protected PlayerBase m_Player;
  16. protected ref DayZPlayerImplementMeleeCombat m_MeleeCombat;
  17. protected EMeleeHitType m_HitType;
  18. protected ref map <int, ref Timer> m_CooldownTimers;
  19. protected Mission m_Mission;
  20. protected bool m_IsInBlock;
  21. protected bool m_IsEvading;
  22. protected bool m_IsInComboRange;
  23. protected bool m_WasPreviousHitProcessed;
  24. void DayZPlayerMeleeFightLogic_LightHeavy(DayZPlayerImplement player)
  25. {
  26. Init(player);
  27. }
  28. void Init(DayZPlayerImplement player)
  29. {
  30. m_DZPlayer = player;
  31. m_Player = PlayerBase.Cast(player);
  32. m_MeleeCombat = m_Player.GetMeleeCombat();
  33. m_Mission = GetGame().GetMission();
  34. m_IsInBlock = false;
  35. m_IsEvading = false;
  36. m_WasPreviousHitProcessed = false;
  37. m_HitType = EMeleeHitType.NONE;
  38. RegisterCooldowns();
  39. }
  40. void ~DayZPlayerMeleeFightLogic_LightHeavy() {}
  41. bool IsInBlock()
  42. {
  43. return m_IsInBlock;
  44. }
  45. void SetBlock(bool block)
  46. {
  47. m_IsInBlock = block;
  48. }
  49. bool IsEvading()
  50. {
  51. return m_IsEvading;
  52. }
  53. bool GetCurrentHitType()
  54. {
  55. return m_HitType;
  56. }
  57. bool CanFight()
  58. {
  59. if (m_Player.IsEmotePlaying())
  60. return false;
  61. if (m_Player.GetActionManager() && m_Player.GetActionManager().GetRunningAction())
  62. return false;
  63. return true;
  64. }
  65. protected void RegisterCooldowns()
  66. {
  67. m_CooldownTimers = new map<int, ref Timer>();
  68. m_CooldownTimers.Insert(EFightLogicCooldownCategory.EVADE, new Timer(CALL_CATEGORY_SYSTEM)); // evades
  69. }
  70. protected EMeleeHitType GetAttackTypeFromInputs(HumanInputController pInputs)
  71. {
  72. if (pInputs.IsMeleeFastAttackModifier() && m_Player.CanConsumeStamina(EStaminaConsumers.MELEE_HEAVY))
  73. {
  74. return EMeleeHitType.HEAVY;
  75. }
  76. return EMeleeHitType.LIGHT;
  77. }
  78. protected EMeleeHitType GetAttackTypeByWeaponAttachments(EntityAI pEntity)
  79. {
  80. if (!m_Player.CanConsumeStamina(EStaminaConsumers.MELEE_HEAVY))
  81. {
  82. return EMeleeHitType.NONE;
  83. }
  84. //! use stabbing if the bayonet/knife is attached to firearm
  85. if (pEntity.HasBayonetAttached())
  86. {
  87. return EMeleeHitType.WPN_STAB;
  88. }
  89. else if (pEntity.HasButtstockAttached())
  90. {
  91. return EMeleeHitType.WPN_HIT_BUTTSTOCK;
  92. }
  93. return EMeleeHitType.WPN_HIT;
  94. }
  95. protected float GetAttackTypeByDistanceToTarget(EntityAI pTarget, EMeleeTargetType pTargetType = EMeleeTargetType.ALIGNABLE)
  96. {
  97. if (pTargetType == EMeleeTargetType.NONALIGNABLE)
  98. {
  99. return 1.0;
  100. }
  101. //! target in short distance - in place attack
  102. if (IsShortDistance(pTarget, Math.SqrFloat(CLOSE_TARGET_DISTANCE)))
  103. {
  104. return 1.0;
  105. }
  106. //! target is far from the player - forward movement attack
  107. return 0.0;
  108. }
  109. bool HandleFightLogic(int pCurrentCommandID, HumanInputController pInputs, EntityAI pEntityInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  110. {
  111. HumanInventoryWithFSM invFSM = HumanInventoryWithFSM.Cast(m_Player.GetHumanInventory());
  112. if (invFSM && !invFSM.IsIdle())
  113. return false;
  114. HumanCommandMove hcm = m_Player.GetCommand_Move();
  115. InventoryItem itemInHands = InventoryItem.Cast(pEntityInHands);
  116. bool isFireWeapon = itemInHands && itemInHands.IsWeapon();
  117. //! Check if we need to damage
  118. if (HandleHitEvent(pCurrentCommandID, pInputs, itemInHands, pMovementState, pContinueAttack))
  119. return false;
  120. //! Actually pressing a button to start a melee attack
  121. if ((pInputs.IsAttackButtonDown() && !isFireWeapon) || (pInputs.IsMeleeWeaponAttack() && isFireWeapon) || (pContinueAttack && isFireWeapon))
  122. {
  123. //Debug.MeleeLog(m_Player, string.Format("HandleFightLogic[1] attackButtonDown=%1, meleeWeaponAttack=%2, pContinueAttack=%3, isFireWeapon=%4", pInputs.IsAttackButtonDown(), pInputs.IsMeleeWeaponAttack(), pContinueAttack, isFireWeapon));
  124. //! do not perform attacks when blocking
  125. if (m_IsInBlock || m_IsEvading)
  126. return false;
  127. //! if the item in hands cannot be used as melee weapon
  128. if (itemInHands && !itemInHands.IsMeleeWeapon() && !isFireWeapon)
  129. return false;
  130. //! Currently not performing any attacks, so here we start the initial
  131. if (pCurrentCommandID == DayZPlayerConstants.COMMANDID_MOVE)
  132. {
  133. //Debug.MeleeLog(m_Player, "HandleFightLogic[1a]");
  134. //! melee with firearm
  135. if (isFireWeapon)
  136. {
  137. return HandleInitialFirearmMelee(pCurrentCommandID, pInputs, itemInHands, pMovementState, pContinueAttack);
  138. }
  139. else
  140. {
  141. //! first attack in raised erc (light or heavy if modifier is used). Also added support for finishers
  142. if (pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDERECT)
  143. {
  144. return HandleInitialMeleeErc(pCurrentCommandID, pInputs, itemInHands, pMovementState, pContinueAttack);
  145. }
  146. //! kick from raised pne
  147. else if (pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDPRONE)
  148. {
  149. return HandleProneKick(pCurrentCommandID, pInputs, itemInHands, pMovementState, pContinueAttack);
  150. }
  151. //! sprint attack in erc stance
  152. else if (pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_ERECT && m_Player.IsSprintFull())
  153. {
  154. return HandleSprintAttack(pCurrentCommandID, pInputs, itemInHands, pMovementState, pContinueAttack);
  155. }
  156. }
  157. }
  158. //! combo hits - when we are already in Melee command and clicking UseButton
  159. else if (pCurrentCommandID == DayZPlayerConstants.COMMANDID_MELEE2)
  160. {
  161. m_IsInComboRange = m_Player.GetCommand_Melee2().IsInComboRange();
  162. if (m_IsInComboRange)
  163. {
  164. return HandleComboHit(pCurrentCommandID, pInputs, itemInHands, pMovementState, pContinueAttack);
  165. }
  166. }
  167. }
  168. //! evade and blocking logic
  169. else if (!isFireWeapon && pCurrentCommandID == DayZPlayerConstants.COMMANDID_MOVE)
  170. {
  171. //! evades in raised erc stance while moving
  172. if (pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDERECT)
  173. {
  174. int roll = pInputs.IsMeleeLREvade();
  175. if (roll != 0)
  176. {
  177. //! not enough stamina to do evades
  178. if (!m_Player.CanConsumeStamina(EStaminaConsumers.MELEE_EVADE))
  179. {
  180. return false;
  181. }
  182. float angle;
  183. if (roll == 1)
  184. {
  185. angle = -90; // left
  186. }
  187. else
  188. {
  189. angle = 90; // right
  190. }
  191. // start melee evade
  192. m_IsEvading = true;
  193. SetCooldown(EVADE_COOLDOWN, EFightLogicCooldownCategory.EVADE);
  194. hcm.StartMeleeEvadeA(angle);
  195. m_Player.DepleteStamina(EStaminaModifiers.MELEE_EVADE);
  196. //Inflict shock when sidestepping with broken legs
  197. if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
  198. {
  199. m_Player.m_ShockHandler.SetShock(PlayerConstants.BROKEN_LEGS_LIGHT_MELEE_SHOCK);
  200. m_Player.m_ShockHandler.CheckValue(true);
  201. }
  202. }
  203. }
  204. //! stand up when crouching and raised pressed
  205. if (pInputs.IsWeaponRaised() && pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_CROUCH)
  206. {
  207. //Debug.MeleeLog(m_Player, "HandleFightLogic[2b]");
  208. if (DayZPlayerUtils.PlayerCanChangeStance(m_Player, DayZPlayerConstants.STANCEIDX_RAISEDERECT))
  209. {
  210. //Debug.MeleeLog(m_Player, "HandleFightLogic[2c]");
  211. hcm.ForceStance(DayZPlayerConstants.STANCEIDX_RAISEDERECT);
  212. return false;
  213. }
  214. }
  215. //! blocks in raised erc/pro stance
  216. //! (bare hand or with melee weapon only)
  217. if (!isFireWeapon && (pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDERECT || pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDPRONE))
  218. {
  219. float angle2;
  220. if (hcm.GetCurrentInputAngle(angle2) && (angle2 < -130.0 || angle2 > 130))
  221. {
  222. hcm.SetMeleeBlock(true);
  223. SetBlock(true);
  224. }
  225. else
  226. {
  227. hcm.SetMeleeBlock(false);
  228. SetBlock(false);
  229. }
  230. }
  231. else
  232. {
  233. hcm.SetMeleeBlock(false);
  234. SetBlock(false);
  235. }
  236. }
  237. return false;
  238. }
  239. //! Handle the event that our melee hit something
  240. protected bool HandleHitEvent(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  241. {
  242. //! Check if we need to damage
  243. if (pCurrentCommandID == DayZPlayerConstants.COMMANDID_MELEE2)
  244. {
  245. HumanCommandMelee2 hmc2a = m_Player.GetCommand_Melee2();
  246. if (hmc2a)
  247. {
  248. //! on anim event Hit received
  249. if (hmc2a.WasHit())
  250. {
  251. //Debug.MeleeLog(m_Player, "HandleHitEvent[START]");
  252. //Debug.MeleeLog(m_Player, string.Format("HandleHitEvent[1] target=%1, hitPos=%2, hitZoneIdx=%3, finisherType=%4", m_MeleeCombat.GetTargetEntity(), m_MeleeCombat.GetHitPos().ToString(), m_MeleeCombat.GetHitZoneIdx(), m_MeleeCombat.GetFinisherType()));
  253. if (m_MeleeCombat.GetFinisherType() == -1)
  254. {
  255. //Debug.MeleeLog(m_Player, string.Format("HandleHitEvent[2] target=%1, hitPos=%2, hitZoneIdx=%3, finisherType=%4", m_MeleeCombat.GetTargetEntity(), m_MeleeCombat.GetHitPos().ToString(), m_MeleeCombat.GetHitZoneIdx(), m_MeleeCombat.GetFinisherType()));
  256. //! re-target (enemy can have moved out of range or disappeared)
  257. m_MeleeCombat.Update(itemInHands, m_HitType, true);
  258. }
  259. //! evaluate hit - selection of cfg 'ammo' type
  260. EvaluateHit(itemInHands);
  261. //Get gloves
  262. ClothingBase gloves;
  263. if (m_HitType == EMeleeHitType.KICK)
  264. {
  265. //We kick so "gloves" will be the shoes
  266. gloves = ClothingBase.Cast(m_Player.GetItemOnSlot("FEET"));
  267. }
  268. else
  269. {
  270. gloves = ClothingBase.Cast(m_Player.GetItemOnSlot("GLOVES"));
  271. }
  272. //If we hit something, inflict damage
  273. DamageHands(m_Player, gloves, itemInHands);
  274. m_MeleeCombat.ResetTarget();
  275. EnableControls();
  276. m_WasPreviousHitProcessed = true;
  277. //Debug.MeleeLog(m_Player, "HandleHitEvent[END]");
  278. return true;
  279. }
  280. }
  281. }
  282. return false;
  283. }
  284. //! NOTE: Only singular (or first) hits, combo attacks are handled in combo
  285. protected bool HandleInitialFirearmMelee(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  286. {
  287. //! don't allow bash to interfere with actions like chambering or ejecting bullets
  288. Weapon_Base weapon = Weapon_Base.Cast(itemInHands);
  289. if (weapon.IsWaitingForActionFinish())
  290. return false;
  291. HumanCommandMove hcm = m_Player.GetCommand_Move();
  292. //! perform firearm melee from raised erect or continue with attack after character stand up from crouch
  293. if (hcm.GetCurrentMovementSpeed() <= 2.05 && (pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDERECT || pContinueAttack) && !m_Player.IsFighting())
  294. {
  295. m_HitType = GetAttackTypeByWeaponAttachments(itemInHands);
  296. if ( m_HitType == EMeleeHitType.NONE )
  297. {
  298. pContinueAttack = false;
  299. return false; //! exit if there is no suitable attack
  300. }
  301. m_MeleeCombat.Update(itemInHands, m_HitType);
  302. EntityAI target;
  303. EMeleeTargetType targetType;
  304. GetTargetData(target, targetType);
  305. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  306. m_Player.StartCommand_Melee2(target, m_HitType == EMeleeHitType.WPN_STAB, attackByDistance, m_MeleeCombat.GetHitPos());
  307. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  308. DisableControls();
  309. pContinueAttack = false; // reset continueAttack flag
  310. return true;
  311. }
  312. //! char stand up when performing firearm melee from crouch
  313. else if ( pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDCROUCH )
  314. {
  315. if (hcm)
  316. {
  317. hcm.ForceStance(DayZPlayerConstants.STANCEIDX_RAISEDERECT);
  318. pContinueAttack = true;
  319. return false;
  320. }
  321. }
  322. return false;
  323. }
  324. //! First attack in raised erc (light or heavy if modifier is used). Also added support for finishers
  325. protected bool HandleInitialMeleeErc(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  326. {
  327. //Debug.MeleeLog(m_Player, "HandleInitialMeleeErc[1]");
  328. m_HitType = GetAttackTypeFromInputs(pInputs);
  329. m_MeleeCombat.Update(itemInHands, m_HitType);
  330. //Debug.MeleeLog(m_Player, string.Format("HandleInitialMeleeErc[2] %1", EnumTools.EnumToString(EMeleeHitType, m_HitType)));
  331. EntityAI target;
  332. EMeleeTargetType targetType;
  333. GetTargetData(target, targetType);
  334. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  335. // finisher attack hittype override
  336. int finisherType = m_MeleeCombat.GetFinisherType();
  337. if (finisherType != -1)
  338. {
  339. int animationType = DetermineFinisherAnimation(finisherType);
  340. if (animationType > -1)
  341. {
  342. //Debug.MeleeLog(m_Player, string.Format("HandleInitialMeleeErc[3a] target=%1, finisherType=%2, animationType=%3, hitPositionWS=%4", target, finisherType, animationType, m_MeleeCombat.GetHitPos().ToString()));
  343. m_Player.StartCommand_Melee2(target, animationType, attackByDistance, m_MeleeCombat.GetHitPos());
  344. }
  345. else
  346. {
  347. //Debug.MeleeLog(m_Player, string.Format("HandleInitialMeleeErc[3b] target=%1, finisherType=%2, animationType=%3, hitPositionWS=%4", target, finisherType, (m_HitType == EMeleeHitType.HEAVY).ToString(), m_MeleeCombat.GetHitPos().ToString()));
  348. m_Player.StartCommand_Melee2(target, m_HitType == EMeleeHitType.HEAVY, attackByDistance, m_MeleeCombat.GetHitPos());
  349. }
  350. m_HitType = finisherType;
  351. target.SetBeingBackstabbed(finisherType);
  352. }
  353. else
  354. {
  355. //Debug.MeleeLog(m_Player, string.Format("HandleInitialMeleeErc[3d] target=%1, animationType=%2, hitPositionWS=%3", target, (m_HitType == EMeleeHitType.HEAVY).ToString(), m_MeleeCombat.GetHitPos().ToString()));
  356. m_Player.StartCommand_Melee2(target, m_HitType == EMeleeHitType.HEAVY, attackByDistance, m_MeleeCombat.GetHitPos());
  357. }
  358. if (m_HitType == EMeleeHitType.HEAVY)
  359. {
  360. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  361. if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
  362. {
  363. m_Player.m_ShockHandler.SetShock(PlayerConstants.BROKEN_LEGS_HEAVY_MELEE_SHOCK);
  364. m_Player.m_ShockHandler.CheckValue(true);
  365. }
  366. }
  367. else
  368. {
  369. m_Player.DepleteStamina(EStaminaModifiers.MELEE_LIGHT);
  370. if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
  371. {
  372. m_Player.m_ShockHandler.SetShock(PlayerConstants.BROKEN_LEGS_LIGHT_MELEE_SHOCK);
  373. m_Player.m_ShockHandler.CheckValue(true);
  374. }
  375. }
  376. m_WasPreviousHitProcessed = false;
  377. DisableControls();
  378. return true;
  379. }
  380. //! kick from raised pne
  381. protected bool HandleProneKick(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  382. {
  383. HumanCommandWeapons hcw = m_Player.GetCommandModifier_Weapons();
  384. float hcw_angle = hcw.GetBaseAimingAngleLR();
  385. //! check if player is on back
  386. //! (situation where the player is raised in prone and on back is not in anim graph)
  387. if ( ( hcw_angle < -90 || hcw_angle > 90 ) && m_Player.GetBrokenLegs() != eBrokenLegs.BROKEN_LEGS )
  388. {
  389. // targetting
  390. m_HitType = EMeleeHitType.KICK;
  391. m_MeleeCombat.Update(itemInHands, m_HitType);
  392. EntityAI target;
  393. EMeleeTargetType targetType;
  394. GetTargetData(target, targetType);
  395. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  396. // command
  397. m_Player.StartCommand_Melee2(target, false, attackByDistance, m_MeleeCombat.GetHitPos());
  398. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  399. DisableControls();
  400. return true;
  401. }
  402. return false;
  403. }
  404. //! sprint attack in erc stance
  405. protected bool HandleSprintAttack(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  406. {
  407. //! targetting
  408. m_HitType = EMeleeHitType.SPRINT;
  409. m_MeleeCombat.Update(itemInHands, m_HitType);
  410. EntityAI target;
  411. EMeleeTargetType targetType;
  412. GetTargetData(target, targetType);
  413. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  414. //! command
  415. //Debug.MeleeLog(m_Player, string.Format("HandleSprintAttack[1] target=%1, animationType=%2, hitPositionWS=%3", target, false, m_MeleeCombat.GetHitPos().ToString()));
  416. m_Player.StartCommand_Melee2(target, false, attackByDistance, m_MeleeCombat.GetHitPos());
  417. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  418. DisableControls();
  419. return true;
  420. }
  421. //! combo hits - when we are already in Melee command and clicking UseButton
  422. protected bool HandleComboHit(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  423. {
  424. //Debug.MeleeLog(m_Player, "HandleComboHit::");
  425. m_IsInComboRange = false;
  426. //! wait for the previous hit commit before hitting again
  427. if (!m_WasPreviousHitProcessed)
  428. return false;
  429. //! select if the next attack will be light or heavy (based on input/attachment modifier)
  430. if (itemInHands && itemInHands.IsWeapon())
  431. m_HitType = GetAttackTypeByWeaponAttachments(itemInHands);
  432. else
  433. m_HitType = GetAttackTypeFromInputs(pInputs);
  434. //! no suitable attack - skip that
  435. if (m_HitType == EMeleeHitType.NONE)
  436. return false;
  437. //! No combos for the finisher command
  438. if (m_MeleeCombat.GetFinisherType() > -1)
  439. return false;
  440. EntityAI target;
  441. EMeleeTargetType targetType;
  442. GetTargetData(target, targetType);
  443. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  444. //! targetting
  445. //Debug.MeleeLog(m_Player, string.Format("HandleComboHit:: %1", EnumTools.EnumToString(EMeleeHitType, m_HitType)));
  446. m_MeleeCombat.Update(itemInHands, m_HitType);
  447. //! continue 'combo' - left hand attacks
  448. bool isHeavy = (m_HitType == EMeleeHitType.HEAVY || m_HitType == EMeleeHitType.WPN_STAB);
  449. m_Player.GetCommand_Melee2().ContinueCombo(isHeavy, attackByDistance, target, m_MeleeCombat.GetHitPos());
  450. DisableControls();
  451. //! broken legs shock
  452. if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
  453. {
  454. float shock = PlayerConstants.BROKEN_LEGS_HEAVY_MELEE_SHOCK;
  455. if (!isHeavy)
  456. shock = PlayerConstants.BROKEN_LEGS_LIGHT_MELEE_SHOCK;
  457. m_Player.m_ShockHandler.SetShock(PlayerConstants.BROKEN_LEGS_HEAVY_MELEE_SHOCK);
  458. m_Player.m_ShockHandler.CheckValue(true);
  459. }
  460. //! stamina depletion per attack
  461. switch (m_HitType)
  462. {
  463. case EMeleeHitType.HEAVY:
  464. case EMeleeHitType.SPRINT:
  465. case EMeleeHitType.WPN_STAB:
  466. case EMeleeHitType.WPN_HIT_BUTTSTOCK:
  467. case EMeleeHitType.WPN_HIT:
  468. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  469. break;
  470. default:
  471. m_Player.DepleteStamina(EStaminaModifiers.MELEE_LIGHT);
  472. break;
  473. }
  474. return true;
  475. }
  476. protected void EvaluateHit(InventoryItem weapon)
  477. {
  478. EntityAI target = m_MeleeCombat.GetTargetEntity();
  479. if (target)
  480. {
  481. if (target.IsInherited(DayZPlayer))
  482. {
  483. EvaluateHit_Player(weapon, target);
  484. }
  485. else if (target.IsInherited(DayZInfected))
  486. {
  487. EvaluateHit_Infected(weapon, target);
  488. }
  489. else if (target.GetMeleeTargetType() == EMeleeTargetType.NONALIGNABLE)
  490. {
  491. EvaluateHit_NonAlignableObjects(weapon, target);
  492. }
  493. else
  494. {
  495. EvaluateHit_Common(weapon, target);
  496. }
  497. m_MeleeCombat.CheckMeleeItem();
  498. }
  499. }
  500. protected void EvaluateHit_Player(InventoryItem weapon, Object target)
  501. {
  502. int hitZoneIdx = m_MeleeCombat.GetHitZoneIdx();
  503. int weaponMode = m_MeleeCombat.GetWeaponMode();
  504. int forcedWeaponMode = -1;
  505. vector hitPosWS;
  506. bool forcedDummy = false;
  507. PlayerBase targetPlayer = PlayerBase.Cast(target);
  508. //! Melee Hit/Impact modifiers
  509. if (targetPlayer)
  510. {
  511. vector targetPos = targetPlayer.GetPosition();
  512. vector agressorPos = m_Player.GetPosition();
  513. // We get the angle from which an infected hit the blocking player
  514. float hitAngle = Math.RAD2DEG * Math.AbsFloat(Math3D.AngleFromPosition(targetPos, MiscGameplayFunctions.GetHeadingVector(targetPlayer), agressorPos));
  515. //! if the oponnent is in Melee Block shift the damage down
  516. if (targetPlayer.GetMeleeFightLogic() && targetPlayer.GetMeleeFightLogic().IsInBlock() && hitAngle <= GameConstants.PVP_MAX_BLOCKABLE_ANGLE)
  517. {
  518. if (weaponMode > 0)
  519. {
  520. forcedWeaponMode = --weaponMode; // Heavy -> Light shift
  521. }
  522. else
  523. forcedDummy = true; // dummy hits, cannot shift lower than 0
  524. }
  525. }
  526. EvaluateHit_Common(weapon, target, forcedDummy, forcedWeaponMode);
  527. }
  528. protected void EvaluateHit_Infected(InventoryItem weapon, Object target)
  529. {
  530. //If the finisher attack haven't been performed, then use normal hit
  531. if (!EvaluateFinisherAttack(weapon, target))
  532. EvaluateHit_Common(weapon, target);
  533. }
  534. //! Check and evaluate stealth kill, if applicable
  535. protected bool EvaluateFinisherAttack(InventoryItem weapon, Object target)
  536. {
  537. //Debug.MeleeLog(m_Player, string.Format("EvaluateFinisherAttack[1] target=%1, hitPos=%2, hitZoneIdx=%3, finisherType=%4", m_MeleeCombat.GetTargetEntity(), m_MeleeCombat.GetHitPos().ToString(), m_MeleeCombat.GetHitZoneIdx(), m_MeleeCombat.GetFinisherType()));
  538. if (m_MeleeCombat.GetFinisherType() > -1)
  539. {
  540. if (GetGame().IsServer())
  541. DamageSystem.CloseCombatDamage(m_Player, target, m_MeleeCombat.GetHitZoneIdx(), DetermineFinisherAmmo(m_MeleeCombat.GetFinisherType()), m_MeleeCombat.GetHitPos(), ProcessDirectDamageFlags.NO_ATTACHMENT_TRANSFER);
  542. return true;
  543. }
  544. return false;
  545. }
  546. protected void EvaluateHit_Common(InventoryItem weapon, Object target, bool forcedDummy=false, int forcedWeaponMode = -1)
  547. {
  548. int hitZoneIdx = m_MeleeCombat.GetHitZoneIdx();
  549. int weaponMode = m_MeleeCombat.GetWeaponMode();
  550. vector hitPosWS;
  551. string ammo;
  552. if (forcedWeaponMode > -1)
  553. weaponMode = forcedWeaponMode;
  554. EntityAI targetEntity = EntityAI.Cast(target);
  555. //! check if we need to use dummy hit
  556. if (!DummyHitSelector(m_HitType, ammo) && !forcedDummy)
  557. {
  558. //! normal hit with applied damage to targeted component
  559. if (hitZoneIdx >= 0)
  560. {
  561. hitPosWS = targetEntity.ModelToWorld(targetEntity.GetDefaultHitPosition());
  562. if (WeaponDestroyedCheck(weapon, ammo))
  563. {
  564. DamageSystem.CloseCombatDamage(m_Player, target, hitZoneIdx, ammo, hitPosWS);
  565. //Debug.MeleeLog(m_Player, string.Format("EvaluateHit_Common[a]::CloseCombatDamage:: target: %1, hitzone: %2, ammo: %3", target, hitZoneIdx, ammo));
  566. }
  567. else
  568. {
  569. m_Player.ProcessMeleeHit(weapon, weaponMode, target, hitZoneIdx, hitPosWS);
  570. //Debug.MeleeLog(m_Player, string.Format("EvaluateHit_Common[b]::ProcessMeleeHit:: target: %1, hitzone: %2, meleeMode: %3, forcedWeaponMode:%4", target, hitZoneIdx, weaponMode));
  571. }
  572. }
  573. }
  574. else
  575. {
  576. //! play hit animation for dummy hits
  577. if (GetGame().IsServer() && targetEntity)
  578. {
  579. DummyHitSelector(m_HitType, ammo);
  580. hitPosWS = targetEntity.ModelToWorld(targetEntity.GetDefaultHitPosition()); //! override hit pos by pos defined in type
  581. DamageSystem.CloseCombatDamage(m_Player, target, hitZoneIdx, ammo, hitPosWS);
  582. //Debug.MeleeLog(m_Player, string.Format("EvaluateHit_Common[c]::CloseCombatDamage:: target: %1, hitzone: %2, meleeMode: %3, ammo: %4", target, hitZoneIdx, weaponMode, ammo));
  583. }
  584. }
  585. }
  586. protected void EvaluateHit_NonAlignableObjects(InventoryItem weapon, Object target)
  587. {
  588. int hitZoneIdx = m_MeleeCombat.GetHitZoneIdx();
  589. vector hitPosWS;
  590. string ammo;
  591. if (hitZoneIdx >= 0)
  592. {
  593. if (WeaponDestroyedCheck(weapon,ammo))
  594. DamageSystem.CloseCombatDamage(m_Player, target, hitZoneIdx, ammo, m_MeleeCombat.GetHitPos());
  595. else
  596. m_Player.ProcessMeleeHit(weapon, m_MeleeCombat.GetWeaponMode(), target, hitZoneIdx, m_MeleeCombat.GetHitPos());
  597. }
  598. return;
  599. }
  600. bool WeaponDestroyedCheck(InventoryItem weapon, out string ammo)
  601. {
  602. if (!weapon)
  603. return false;
  604. Weapon_Base firearm = Weapon_Base.Cast(weapon);
  605. ItemBase bayonet = ItemBase.Cast(weapon.GetInventory().FindAttachment(weapon.GetBayonetAttachmentIdx()));
  606. if ( firearm && bayonet && bayonet.IsRuined() )
  607. {
  608. ammo = bayonet.GetRuinedMeleeAmmoType();
  609. return true;
  610. }
  611. else if (weapon.IsRuined())
  612. {
  613. ammo = weapon.GetRuinedMeleeAmmoType();
  614. return true;
  615. }
  616. else
  617. {
  618. return false;
  619. }
  620. }
  621. protected void GetTargetData(out EntityAI target, out EMeleeTargetType targetType)
  622. {
  623. target = m_MeleeCombat.GetTargetEntity();
  624. targetType = EMeleeTargetType.ALIGNABLE; //! default
  625. // If there is no hitzone defined, then we won't be able to hit the target anyways..
  626. if (m_MeleeCombat.GetHitZoneIdx() < 0)
  627. {
  628. target = null;
  629. }
  630. if ( target )
  631. {
  632. targetType = target.GetMeleeTargetType();
  633. }
  634. //! nullify target for nonalignable objects (big objects)
  635. if ( targetType == EMeleeTargetType.NONALIGNABLE )
  636. {
  637. target = null;
  638. }
  639. }
  640. //! evaluation of hit player vs. player
  641. protected bool DummyHitSelector(EMeleeHitType hitType, out string ammoType)
  642. {
  643. switch (hitType)
  644. {
  645. //! in case of kick (on back or push from erc) change the ammo type to dummy
  646. case EMeleeHitType.KICK:
  647. ammoType = DUMMY_HEAVY_AMMO;
  648. return true;
  649. break;
  650. }
  651. ammoType = DUMMY_LIGHT_AMMO;
  652. return false;
  653. }
  654. //! DEPRECATED - moved into DayZPlayerImplementMeleeCombat
  655. protected bool IsBehindEntity(int angle, DayZPlayer source, Object target)
  656. {
  657. return false;
  658. }
  659. protected void SetCooldown(float time, EFightLogicCooldownCategory cooldownCategory)
  660. {
  661. //! stops currently running cooldown timer (for specific category)
  662. if ( m_CooldownTimers.Get(cooldownCategory).IsRunning() )
  663. {
  664. m_CooldownTimers.Get(cooldownCategory).Stop();
  665. }
  666. //! param for ResetCooldown
  667. Param1<int> param = new Param1<int>(cooldownCategory);
  668. m_CooldownTimers.Get(cooldownCategory).Run(time, this, "ResetCooldown", param);
  669. }
  670. protected void ResetCooldown(EFightLogicCooldownCategory cooldownCategory)
  671. {
  672. switch (cooldownCategory)
  673. {
  674. case EFightLogicCooldownCategory.EVADE:
  675. m_IsEvading = false;
  676. break;
  677. }
  678. }
  679. protected void EnableControls();
  680. protected void DisableControls();
  681. protected void DamageHands(DayZPlayer DZPlayer, ClothingBase gloves, InventoryItem itemInHands)
  682. {
  683. EntityAI target = m_MeleeCombat.GetTargetEntity();
  684. //We did not hit anything
  685. if (itemInHands || !target || !DZPlayer)
  686. return;
  687. //Check if server side
  688. if (GetGame().IsServer())
  689. {
  690. int randNum;
  691. //If gloves, damage gloves
  692. if (gloves && gloves.GetHealthLevel() < GameConstants.STATE_RUINED)
  693. {
  694. gloves.DecreaseHealth("", "", 1);
  695. }
  696. else
  697. {
  698. //Do not add bleeding if hitting player, zombie or animal
  699. if (PlayerBase.Cast(target) || DayZCreatureAI.Cast(target))
  700. return;
  701. BleedingSourcesManagerServer bleedingManager;
  702. //We don't inflict bleeding to hands when kicking
  703. if (m_HitType != EMeleeHitType.KICK)
  704. {
  705. //Random bleeding source
  706. randNum = Math.RandomIntInclusive(1, 15);
  707. switch (randNum)
  708. {
  709. case 1:
  710. if (m_Player)
  711. {
  712. bleedingManager = m_Player.GetBleedingManagerServer();
  713. if (bleedingManager)
  714. bleedingManager.AttemptAddBleedingSourceBySelection("RightForeArmRoll");
  715. }
  716. break;
  717. case 2:
  718. if (m_Player)
  719. {
  720. bleedingManager = m_Player.GetBleedingManagerServer();
  721. if (bleedingManager)
  722. bleedingManager.AttemptAddBleedingSourceBySelection("LeftForeArmRoll");
  723. }
  724. break;
  725. }
  726. }
  727. else
  728. {
  729. //Random bleeding source
  730. randNum = Math.RandomIntInclusive(1, 15);
  731. //We only add bleeding to left foot as character kicks with left foot
  732. switch (randNum)
  733. {
  734. case 1:
  735. if (m_Player)
  736. {
  737. bleedingManager = m_Player.GetBleedingManagerServer();
  738. if (bleedingManager)
  739. bleedingManager.AttemptAddBleedingSourceBySelection("LeftToeBase");
  740. }
  741. break;
  742. case 2:
  743. if (m_Player)
  744. {
  745. bleedingManager = m_Player.GetBleedingManagerServer();
  746. if (bleedingManager)
  747. bleedingManager.AttemptAddBleedingSourceBySelection("LeftFoot");
  748. }
  749. break;
  750. }
  751. }
  752. }
  753. }
  754. }
  755. //! Picks a specific finisher animation. Not used for mounted bayonet stabs
  756. int DetermineFinisherAnimation(int finisher_type)
  757. {
  758. int animation = -1;
  759. switch (finisher_type)
  760. {
  761. case EMeleeHitType.FINISHER_LIVERSTAB:
  762. animation = HumanCommandMelee2.HIT_TYPE_FINISHER;
  763. break;
  764. case EMeleeHitType.FINISHER_NECKSTAB:
  765. animation = HumanCommandMelee2.HIT_TYPE_FINISHER_NECK;
  766. break;
  767. }
  768. return animation;
  769. }
  770. //! Picks a specific finisher ammo fot the hit type. This gets synchronized and guides further behaviour of the target.
  771. string DetermineFinisherAmmo(int finisher_type)
  772. {
  773. string ret = "";
  774. switch (finisher_type)
  775. {
  776. case EMeleeHitType.FINISHER_NECKSTAB:
  777. ret = "FinisherHitNeck";
  778. break;
  779. default:
  780. ret = "FinisherHit";
  781. break;
  782. }
  783. return ret;
  784. }
  785. protected bool IsShortDistance(EntityAI pTarget, float pDistanceSq)
  786. {
  787. return pTarget && vector.DistanceSq(m_Player.GetPosition(), m_MeleeCombat.GetHitPos()) <= pDistanceSq || vector.DistanceSq(m_Player.GetBonePositionWS(m_Player.GetBoneIndexByName("Head")), m_MeleeCombat.GetHitPos()) <= pDistanceSq)
  788. }
  789. //!
  790. //! DEPRECATED
  791. protected DayZPlayerImplement m_DZPlayer;
  792. protected bool m_IsFinisher;
  793. int GetFinisherType(InventoryItem weapon, EntityAI target)
  794. {
  795. return -1;
  796. }
  797. bool IsHitTypeFinisher(int type)
  798. {
  799. return false;
  800. }
  801. int DetermineSpecificFinisherType(ItemBase weapon, int component)
  802. {
  803. return -1;
  804. }
  805. }