dayzplayermeleefightlogic_lightheavy.c 29 KB

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