dayzplayermeleefightlogic_lightheavy.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  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. return false; //! exit if there is no suitable attack
  298. m_MeleeCombat.Update(itemInHands, m_HitType);
  299. EntityAI target;
  300. EMeleeTargetType targetType;
  301. GetTargetData(target, targetType);
  302. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  303. m_Player.StartCommand_Melee2(target, m_HitType == EMeleeHitType.WPN_STAB, attackByDistance, m_MeleeCombat.GetHitPos());
  304. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  305. DisableControls();
  306. pContinueAttack = false; // reset continueAttack flag
  307. return true;
  308. }
  309. //! char stand up when performing firearm melee from crouch
  310. else if ( pMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_RAISEDCROUCH )
  311. {
  312. if (hcm)
  313. {
  314. hcm.ForceStance(DayZPlayerConstants.STANCEIDX_RAISEDERECT);
  315. pContinueAttack = true;
  316. return false;
  317. }
  318. }
  319. return false;
  320. }
  321. //! First attack in raised erc (light or heavy if modifier is used). Also added support for finishers
  322. protected bool HandleInitialMeleeErc(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  323. {
  324. //Debug.MeleeLog(m_Player, "HandleInitialMeleeErc[1]");
  325. m_HitType = GetAttackTypeFromInputs(pInputs);
  326. m_MeleeCombat.Update(itemInHands, m_HitType);
  327. //Debug.MeleeLog(m_Player, string.Format("HandleInitialMeleeErc[2] %1", EnumTools.EnumToString(EMeleeHitType, m_HitType)));
  328. EntityAI target;
  329. EMeleeTargetType targetType;
  330. GetTargetData(target, targetType);
  331. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  332. // finisher attack hittype override
  333. int finisherType = m_MeleeCombat.GetFinisherType();
  334. if (finisherType != -1)
  335. {
  336. int animationType = DetermineFinisherAnimation(finisherType);
  337. if (animationType > -1)
  338. {
  339. //Debug.MeleeLog(m_Player, string.Format("HandleInitialMeleeErc[3a] target=%1, finisherType=%2, animationType=%3, hitPositionWS=%4", target, finisherType, animationType, m_MeleeCombat.GetHitPos().ToString()));
  340. m_Player.StartCommand_Melee2(target, animationType, attackByDistance, m_MeleeCombat.GetHitPos());
  341. }
  342. else
  343. {
  344. //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()));
  345. m_Player.StartCommand_Melee2(target, m_HitType == EMeleeHitType.HEAVY, attackByDistance, m_MeleeCombat.GetHitPos());
  346. }
  347. m_HitType = finisherType;
  348. target.SetBeingBackstabbed(finisherType);
  349. }
  350. else
  351. {
  352. //Debug.MeleeLog(m_Player, string.Format("HandleInitialMeleeErc[3d] target=%1, animationType=%2, hitPositionWS=%3", target, (m_HitType == EMeleeHitType.HEAVY).ToString(), m_MeleeCombat.GetHitPos().ToString()));
  353. m_Player.StartCommand_Melee2(target, m_HitType == EMeleeHitType.HEAVY, attackByDistance, m_MeleeCombat.GetHitPos());
  354. }
  355. if (m_HitType == EMeleeHitType.HEAVY)
  356. {
  357. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  358. if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
  359. {
  360. m_Player.m_ShockHandler.SetShock(PlayerConstants.BROKEN_LEGS_HEAVY_MELEE_SHOCK);
  361. m_Player.m_ShockHandler.CheckValue(true);
  362. }
  363. }
  364. else
  365. {
  366. m_Player.DepleteStamina(EStaminaModifiers.MELEE_LIGHT);
  367. if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
  368. {
  369. m_Player.m_ShockHandler.SetShock(PlayerConstants.BROKEN_LEGS_LIGHT_MELEE_SHOCK);
  370. m_Player.m_ShockHandler.CheckValue(true);
  371. }
  372. }
  373. m_WasPreviousHitProcessed = false;
  374. DisableControls();
  375. return true;
  376. }
  377. //! kick from raised pne
  378. protected bool HandleProneKick(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  379. {
  380. HumanCommandWeapons hcw = m_Player.GetCommandModifier_Weapons();
  381. float hcw_angle = hcw.GetBaseAimingAngleLR();
  382. //! check if player is on back
  383. //! (situation where the player is raised in prone and on back is not in anim graph)
  384. if ( ( hcw_angle < -90 || hcw_angle > 90 ) && m_Player.GetBrokenLegs() != eBrokenLegs.BROKEN_LEGS )
  385. {
  386. // targetting
  387. m_HitType = EMeleeHitType.KICK;
  388. m_MeleeCombat.Update(itemInHands, m_HitType);
  389. EntityAI target;
  390. EMeleeTargetType targetType;
  391. GetTargetData(target, targetType);
  392. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  393. // command
  394. m_Player.StartCommand_Melee2(target, false, attackByDistance, m_MeleeCombat.GetHitPos());
  395. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  396. DisableControls();
  397. return true;
  398. }
  399. return false;
  400. }
  401. //! sprint attack in erc stance
  402. protected bool HandleSprintAttack(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  403. {
  404. //! targetting
  405. m_HitType = EMeleeHitType.SPRINT;
  406. m_MeleeCombat.Update(itemInHands, m_HitType);
  407. EntityAI target;
  408. EMeleeTargetType targetType;
  409. GetTargetData(target, targetType);
  410. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  411. //! command
  412. //Debug.MeleeLog(m_Player, string.Format("HandleSprintAttack[1] target=%1, animationType=%2, hitPositionWS=%3", target, false, m_MeleeCombat.GetHitPos().ToString()));
  413. m_Player.StartCommand_Melee2(target, false, attackByDistance, m_MeleeCombat.GetHitPos());
  414. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  415. DisableControls();
  416. return true;
  417. }
  418. //! combo hits - when we are already in Melee command and clicking UseButton
  419. protected bool HandleComboHit(int pCurrentCommandID, HumanInputController pInputs, InventoryItem itemInHands, HumanMovementState pMovementState, out bool pContinueAttack)
  420. {
  421. //Debug.MeleeLog(m_Player, "HandleComboHit::");
  422. //! no suitable attack - skip that
  423. if (m_HitType == EMeleeHitType.NONE)
  424. return false;
  425. //! No combos for the finisher command
  426. if (m_MeleeCombat.GetFinisherType() > -1)
  427. return false;
  428. EntityAI target;
  429. EMeleeTargetType targetType;
  430. GetTargetData(target, targetType);
  431. float attackByDistance = GetAttackTypeByDistanceToTarget(target, targetType);
  432. //! select if the next attack will light or heavy (based on input/attachment modifier)
  433. if (itemInHands && itemInHands.IsWeapon())
  434. {
  435. m_HitType = GetAttackTypeByWeaponAttachments(itemInHands);
  436. }
  437. else
  438. {
  439. m_HitType = GetAttackTypeFromInputs(pInputs);
  440. }
  441. //! targetting
  442. //Debug.MeleeLog(m_Player, string.Format("HandleComboHit:: %1", EnumTools.EnumToString(EMeleeHitType, m_HitType)));
  443. //! wait for the previous hit commit before hitting again
  444. if (m_WasPreviousHitProcessed)
  445. {
  446. m_MeleeCombat.Update(itemInHands, m_HitType);
  447. }
  448. //! continue 'combo' - left hand attacks
  449. bool isHeavy = (m_HitType == EMeleeHitType.HEAVY || m_HitType == EMeleeHitType.WPN_STAB);
  450. m_Player.GetCommand_Melee2().ContinueCombo(isHeavy, attackByDistance, target, m_MeleeCombat.GetHitPos());
  451. DisableControls();
  452. //! broken legs shock
  453. if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
  454. {
  455. float shock = PlayerConstants.BROKEN_LEGS_HEAVY_MELEE_SHOCK;
  456. if (!isHeavy)
  457. shock = PlayerConstants.BROKEN_LEGS_LIGHT_MELEE_SHOCK;
  458. m_Player.m_ShockHandler.SetShock(PlayerConstants.BROKEN_LEGS_HEAVY_MELEE_SHOCK);
  459. m_Player.m_ShockHandler.CheckValue(true);
  460. }
  461. //! stamina depletion per attack
  462. switch (m_HitType)
  463. {
  464. case EMeleeHitType.HEAVY:
  465. case EMeleeHitType.SPRINT:
  466. case EMeleeHitType.WPN_STAB:
  467. case EMeleeHitType.WPN_HIT_BUTTSTOCK:
  468. case EMeleeHitType.WPN_HIT:
  469. m_Player.DepleteStamina(EStaminaModifiers.MELEE_HEAVY);
  470. break;
  471. default:
  472. m_Player.DepleteStamina(EStaminaModifiers.MELEE_LIGHT);
  473. break;
  474. }
  475. m_IsInComboRange = false;
  476. return true;
  477. }
  478. protected void EvaluateHit(InventoryItem weapon)
  479. {
  480. EntityAI target = m_MeleeCombat.GetTargetEntity();
  481. if (target)
  482. {
  483. if (target.IsInherited(DayZPlayer))
  484. {
  485. EvaluateHit_Player(weapon, target);
  486. }
  487. else if (target.IsInherited(DayZInfected))
  488. {
  489. EvaluateHit_Infected(weapon, target);
  490. }
  491. else if (target.GetMeleeTargetType() == EMeleeTargetType.NONALIGNABLE)
  492. {
  493. EvaluateHit_NonAlignableObjects(weapon, target);
  494. }
  495. else
  496. {
  497. EvaluateHit_Common(weapon, target);
  498. }
  499. m_MeleeCombat.CheckMeleeItem();
  500. }
  501. }
  502. protected void EvaluateHit_Player(InventoryItem weapon, Object target)
  503. {
  504. int hitZoneIdx = m_MeleeCombat.GetHitZoneIdx();
  505. int weaponMode = m_MeleeCombat.GetWeaponMode();
  506. int forcedWeaponMode = -1;
  507. vector hitPosWS;
  508. bool forcedDummy = false;
  509. PlayerBase targetPlayer = PlayerBase.Cast(target);
  510. //! Melee Hit/Impact modifiers
  511. if (targetPlayer)
  512. {
  513. vector targetPos = targetPlayer.GetPosition();
  514. vector agressorPos = m_Player.GetPosition();
  515. // We get the angle from which an infected hit the blocking player
  516. float hitAngle = Math.RAD2DEG * Math.AbsFloat(Math3D.AngleFromPosition(targetPos, MiscGameplayFunctions.GetHeadingVector(targetPlayer), agressorPos));
  517. //! if the oponnent is in Melee Block shift the damage down
  518. if (targetPlayer.GetMeleeFightLogic() && targetPlayer.GetMeleeFightLogic().IsInBlock() && hitAngle <= GameConstants.PVP_MAX_BLOCKABLE_ANGLE)
  519. {
  520. if (weaponMode > 0)
  521. {
  522. forcedWeaponMode = --weaponMode; // Heavy -> Light shift
  523. }
  524. else
  525. forcedDummy = true; // dummy hits, cannot shift lower than 0
  526. }
  527. }
  528. EvaluateHit_Common(weapon, target, forcedDummy, forcedWeaponMode);
  529. }
  530. protected void EvaluateHit_Infected(InventoryItem weapon, Object target)
  531. {
  532. //If the finisher attack haven't been performed, then use normal hit
  533. if (!EvaluateFinisherAttack(weapon, target))
  534. EvaluateHit_Common(weapon, target);
  535. }
  536. //! Check and evaluate stealth kill, if applicable
  537. protected bool EvaluateFinisherAttack(InventoryItem weapon, Object target)
  538. {
  539. //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()));
  540. if (m_MeleeCombat.GetFinisherType() > -1)
  541. {
  542. if (GetGame().IsServer())
  543. DamageSystem.CloseCombatDamage(m_Player, target, m_MeleeCombat.GetHitZoneIdx(), DetermineFinisherAmmo(m_MeleeCombat.GetFinisherType()), m_MeleeCombat.GetHitPos(), ProcessDirectDamageFlags.NO_ATTACHMENT_TRANSFER);
  544. return true;
  545. }
  546. return false;
  547. }
  548. protected void EvaluateHit_Common(InventoryItem weapon, Object target, bool forcedDummy=false, int forcedWeaponMode = -1)
  549. {
  550. int hitZoneIdx = m_MeleeCombat.GetHitZoneIdx();
  551. int weaponMode = m_MeleeCombat.GetWeaponMode();
  552. vector hitPosWS;
  553. string ammo;
  554. if (forcedWeaponMode > -1)
  555. weaponMode = forcedWeaponMode;
  556. EntityAI targetEntity = EntityAI.Cast(target);
  557. //! check if we need to use dummy hit
  558. if (!DummyHitSelector(m_HitType, ammo) && !forcedDummy)
  559. {
  560. //! normal hit with applied damage to targeted component
  561. if (hitZoneIdx >= 0)
  562. {
  563. hitPosWS = targetEntity.ModelToWorld(targetEntity.GetDefaultHitPosition());
  564. if (WeaponDestroyedCheck(weapon, ammo))
  565. {
  566. DamageSystem.CloseCombatDamage(m_Player, target, hitZoneIdx, ammo, hitPosWS);
  567. //Debug.MeleeLog(m_Player, string.Format("EvaluateHit_Common[a]::CloseCombatDamage:: target: %1, hitzone: %2, ammo: %3", target, hitZoneIdx, ammo));
  568. }
  569. else
  570. {
  571. m_Player.ProcessMeleeHit(weapon, weaponMode, target, hitZoneIdx, hitPosWS);
  572. //Debug.MeleeLog(m_Player, string.Format("EvaluateHit_Common[b]::ProcessMeleeHit:: target: %1, hitzone: %2, meleeMode: %3, forcedWeaponMode:%4", target, hitZoneIdx, weaponMode));
  573. }
  574. }
  575. }
  576. else
  577. {
  578. //! play hit animation for dummy hits
  579. if (GetGame().IsServer() && targetEntity)
  580. {
  581. DummyHitSelector(m_HitType, ammo);
  582. hitPosWS = targetEntity.ModelToWorld(targetEntity.GetDefaultHitPosition()); //! override hit pos by pos defined in type
  583. DamageSystem.CloseCombatDamage(m_Player, target, hitZoneIdx, ammo, hitPosWS);
  584. //Debug.MeleeLog(m_Player, string.Format("EvaluateHit_Common[c]::CloseCombatDamage:: target: %1, hitzone: %2, meleeMode: %3, ammo: %4", target, hitZoneIdx, weaponMode, ammo));
  585. }
  586. }
  587. }
  588. protected void EvaluateHit_NonAlignableObjects(InventoryItem weapon, Object target)
  589. {
  590. int hitZoneIdx = m_MeleeCombat.GetHitZoneIdx();
  591. vector hitPosWS;
  592. string ammo;
  593. if (hitZoneIdx >= 0)
  594. {
  595. if (WeaponDestroyedCheck(weapon,ammo))
  596. DamageSystem.CloseCombatDamage(m_Player, target, hitZoneIdx, ammo, m_MeleeCombat.GetHitPos());
  597. else
  598. m_Player.ProcessMeleeHit(weapon, m_MeleeCombat.GetWeaponMode(), target, hitZoneIdx, m_MeleeCombat.GetHitPos());
  599. }
  600. return;
  601. }
  602. bool WeaponDestroyedCheck(InventoryItem weapon, out string ammo)
  603. {
  604. if (!weapon)
  605. return false;
  606. Weapon_Base firearm = Weapon_Base.Cast(weapon);
  607. ItemBase bayonet = ItemBase.Cast(weapon.GetInventory().FindAttachment(weapon.GetBayonetAttachmentIdx()));
  608. if ( firearm && bayonet && bayonet.IsRuined() )
  609. {
  610. ammo = bayonet.GetRuinedMeleeAmmoType();
  611. return true;
  612. }
  613. else if (weapon.IsRuined())
  614. {
  615. ammo = weapon.GetRuinedMeleeAmmoType();
  616. return true;
  617. }
  618. else
  619. {
  620. return false;
  621. }
  622. }
  623. protected void GetTargetData(out EntityAI target, out EMeleeTargetType targetType)
  624. {
  625. target = m_MeleeCombat.GetTargetEntity();
  626. targetType = EMeleeTargetType.ALIGNABLE; //! default
  627. // If there is no hitzone defined, then we won't be able to hit the target anyways..
  628. if (m_MeleeCombat.GetHitZoneIdx() < 0)
  629. {
  630. target = null;
  631. }
  632. if ( target )
  633. {
  634. targetType = target.GetMeleeTargetType();
  635. }
  636. //! nullify target for nonalignable objects (big objects)
  637. if ( targetType == EMeleeTargetType.NONALIGNABLE )
  638. {
  639. target = null;
  640. }
  641. }
  642. //! evaluation of hit player vs. player
  643. protected bool DummyHitSelector(EMeleeHitType hitType, out string ammoType)
  644. {
  645. switch (hitType)
  646. {
  647. //! in case of kick (on back or push from erc) change the ammo type to dummy
  648. case EMeleeHitType.KICK:
  649. ammoType = DUMMY_HEAVY_AMMO;
  650. return true;
  651. break;
  652. }
  653. ammoType = DUMMY_LIGHT_AMMO;
  654. return false;
  655. }
  656. //! DEPRECATED - moved into DayZPlayerImplementMeleeCombat
  657. protected bool IsBehindEntity(int angle, DayZPlayer source, Object target)
  658. {
  659. return false;
  660. }
  661. protected void SetCooldown(float time, EFightLogicCooldownCategory cooldownCategory)
  662. {
  663. //! stops currently running cooldown timer (for specific category)
  664. if ( m_CooldownTimers.Get(cooldownCategory).IsRunning() )
  665. {
  666. m_CooldownTimers.Get(cooldownCategory).Stop();
  667. }
  668. //! param for ResetCooldown
  669. Param1<int> param = new Param1<int>(cooldownCategory);
  670. m_CooldownTimers.Get(cooldownCategory).Run(time, this, "ResetCooldown", param);
  671. }
  672. protected void ResetCooldown(EFightLogicCooldownCategory cooldownCategory)
  673. {
  674. switch (cooldownCategory)
  675. {
  676. case EFightLogicCooldownCategory.EVADE:
  677. m_IsEvading = false;
  678. break;
  679. }
  680. }
  681. protected void EnableControls();
  682. protected void DisableControls();
  683. protected void DamageHands(DayZPlayer DZPlayer, ClothingBase gloves, InventoryItem itemInHands)
  684. {
  685. EntityAI target = m_MeleeCombat.GetTargetEntity();
  686. //We did not hit anything
  687. if (itemInHands || !target || !DZPlayer)
  688. return;
  689. //Check if server side
  690. if (GetGame().IsServer())
  691. {
  692. int randNum;
  693. //If gloves, damage gloves
  694. if (gloves && gloves.GetHealthLevel() < GameConstants.STATE_RUINED)
  695. {
  696. gloves.DecreaseHealth("", "", 1);
  697. }
  698. else
  699. {
  700. //Do not add bleeding if hitting player, zombie or animal
  701. if (PlayerBase.Cast(target) || DayZCreatureAI.Cast(target))
  702. return;
  703. BleedingSourcesManagerServer bleedingManager;
  704. //We don't inflict bleeding to hands when kicking
  705. if (m_HitType != EMeleeHitType.KICK)
  706. {
  707. //Random bleeding source
  708. randNum = Math.RandomIntInclusive(1, 15);
  709. switch (randNum)
  710. {
  711. case 1:
  712. if (m_Player)
  713. {
  714. bleedingManager = m_Player.GetBleedingManagerServer();
  715. if (bleedingManager)
  716. bleedingManager.AttemptAddBleedingSourceBySelection("RightForeArmRoll");
  717. }
  718. break;
  719. case 2:
  720. if (m_Player)
  721. {
  722. bleedingManager = m_Player.GetBleedingManagerServer();
  723. if (bleedingManager)
  724. bleedingManager.AttemptAddBleedingSourceBySelection("LeftForeArmRoll");
  725. }
  726. break;
  727. }
  728. }
  729. else
  730. {
  731. //Random bleeding source
  732. randNum = Math.RandomIntInclusive(1, 15);
  733. //We only add bleeding to left foot as character kicks with left foot
  734. switch (randNum)
  735. {
  736. case 1:
  737. if (m_Player)
  738. {
  739. bleedingManager = m_Player.GetBleedingManagerServer();
  740. if (bleedingManager)
  741. bleedingManager.AttemptAddBleedingSourceBySelection("LeftToeBase");
  742. }
  743. break;
  744. case 2:
  745. if (m_Player)
  746. {
  747. bleedingManager = m_Player.GetBleedingManagerServer();
  748. if (bleedingManager)
  749. bleedingManager.AttemptAddBleedingSourceBySelection("LeftFoot");
  750. }
  751. break;
  752. }
  753. }
  754. }
  755. }
  756. }
  757. //! Picks a specific finisher animation. Not used for mounted bayonet stabs
  758. int DetermineFinisherAnimation(int finisher_type)
  759. {
  760. int animation = -1;
  761. switch (finisher_type)
  762. {
  763. case EMeleeHitType.FINISHER_LIVERSTAB:
  764. animation = HumanCommandMelee2.HIT_TYPE_FINISHER;
  765. break;
  766. case EMeleeHitType.FINISHER_NECKSTAB:
  767. animation = HumanCommandMelee2.HIT_TYPE_FINISHER_NECK;
  768. break;
  769. }
  770. return animation;
  771. }
  772. //! Picks a specific finisher ammo fot the hit type. This gets synchronized and guides further behaviour of the target.
  773. string DetermineFinisherAmmo(int finisher_type)
  774. {
  775. string ret = "";
  776. switch (finisher_type)
  777. {
  778. case EMeleeHitType.FINISHER_NECKSTAB:
  779. ret = "FinisherHitNeck";
  780. break;
  781. default:
  782. ret = "FinisherHit";
  783. break;
  784. }
  785. return ret;
  786. }
  787. protected bool IsShortDistance(EntityAI pTarget, float pDistanceSq)
  788. {
  789. return pTarget && vector.DistanceSq(m_Player.GetPosition(), m_MeleeCombat.GetHitPos()) <= pDistanceSq || vector.DistanceSq(m_Player.GetBonePositionWS(m_Player.GetBoneIndexByName("Head")), m_MeleeCombat.GetHitPos()) <= pDistanceSq)
  790. }
  791. //!
  792. //! DEPRECATED
  793. protected DayZPlayerImplement m_DZPlayer;
  794. protected bool m_IsFinisher;
  795. int GetFinisherType(InventoryItem weapon, EntityAI target)
  796. {
  797. return -1;
  798. }
  799. bool IsHitTypeFinisher(int type)
  800. {
  801. return false;
  802. }
  803. int DetermineSpecificFinisherType(ItemBase weapon, int component)
  804. {
  805. return -1;
  806. }
  807. }