48 m_fDamageOverTime = DamageOverTime;
51 float m_fDamageOverTime;
57 protected static const float DAMAGE_TO_BLOOD_MULTIPLIER = 1.3;
59 [
Attribute(
"0.25", UIWidgets.Auto,
"Multiplier of hitzone health that will be applied as bleeding damage over time when damaged")]
60 protected float m_fBleedingRateScale;
63 protected ref array<string> m_aDamageSubmeshes;
66 protected ref array<ref LoadoutAreaType> m_aBleedingAreas;
74 protected bool m_bIsWounded;
91 super.OnDamage(damageContext);
93 if (
this != damageContext.struckHitZone)
97 bool critical = damageContext.damageType ==
EDamageType.FIRE || damageContext.damageValue >= GetCriticalDamageThreshold() *
GetMaxHealth();
101 manager.SoundHit(critical, damageContext.damageType);
112 AddBloodToClothes(Math.Clamp(damageContext.damageValue * DAMAGE_TO_BLOOD_MULTIPLIER, 0, 255));
117 if (Math.RandomFloat(0,1) < 0.3)
120 AddBleeding(damageContext.colliderID);
140 override float ComputeEffectiveDamage(notnull
BaseDamageContext damageContext,
bool isDOT)
142 float rawDamage = damageContext.damageValue;
143 if (damageContext.damageValue > 0 && !isDOT)
145 float protectionValue = GetArmorProtectionValue(damageContext.damageType);
146 rawDamage = Math.Max(damageContext.damageValue - protectionValue, 0);
150 hack.damageValue = rawDamage;
152 return super.ComputeEffectiveDamage(hack, isDOT);
157 float GetArmorProtectionValue(
EDamageType damageType)
163 return damageMgr.GetArmorProtection(
this, damageType);
170 CharacterIdentityComponent identity = CharacterIdentityComponent.Cast(
GetOwner().FindComponent(CharacterIdentityComponent));
174 foreach (
string submesh: m_aDamageSubmeshes)
176 if (identity.IsCovered(submesh))
188 void AddBleeding(
int colliderID = -1)
190 if (!HasColliderNodes())
193 int colliderDescriptorIndex = GetColliderDescriptorIndex(colliderID);
194 if (colliderDescriptorIndex < 0)
197 colliderDescriptorIndex = currentHealth % GetNumColliderDescriptors();
200 AddBleedingToCollider(colliderDescriptorIndex);
208 void AddBleedingToCollider(
int colliderDescriptorIndex = -1)
210 if (!HasColliderNodes())
216 manager.AddBleedingHitZone(
this, colliderDescriptorIndex);
220 override void OnDamageStateChanged()
222 super.OnDamageStateChanged();
227 manager.UpdateCharacterGroupDamage(GetHitZoneGroup());
231 void AddBloodToClothes(
float immediateBloodEffect = 0)
233 EquipedLoadoutStorageComponent loadoutStorage = EquipedLoadoutStorageComponent.Cast(
GetOwner().FindComponent(EquipedLoadoutStorageComponent));
239 for (
int i = m_aBleedingAreas.Count() - 1; i >= 0; i--)
241 clothEntity = loadoutStorage.GetClothFromArea(m_aBleedingAreas[i].Type());
246 if (!materialComponent)
249 if (immediateBloodEffect > 0)
251 materialComponent.SetUserParam2(Math.Clamp(materialComponent.GetUserParam2() + immediateBloodEffect, 1, 255));
255 materialComponent.SetUserParam2(Math.Clamp(materialComponent.GetUserParam2() + GetMaxBleedingRate() * 0.1, 1, 255));
260 void SetWoundedSubmesh(
bool wounded)
262 m_bIsWounded = wounded;
264 CharacterIdentityComponent identity = CharacterIdentityComponent.Cast(
GetOwner().FindComponent(CharacterIdentityComponent));
268 foreach (
string submesh: m_aDamageSubmeshes)
269 identity.SetWoundState(submesh, wounded);
274 void UpdateSubmeshes()
276 bool isWounded = GetDamageStateThreshold(GetDamageState()) <= GetDamageStateThreshold(
ECharacterDamageState.WOUNDED);
277 if (m_bIsWounded != isWounded)
278 SetWoundedSubmesh(isWounded);
285 float GetMaxBleedingRate()
291 float bleedingRate = m_fBleedingRateScale *
GetMaxHealth();
293 bleedingRate *= manager.GetDOTScale();
299 override EHitZoneGroup GetHitZoneGroup()
307 return m_eBandageAnimationBodyPart;
314 [
Attribute(
"10", UIWidgets.Auto,
"Time without receiving damage or bleeding to start regeneration\n[s]")]
315 protected float m_fRegenerationDelay;
317 [
Attribute(
"200", UIWidgets.Auto,
"Time to regenerate this hitzone fully\n[s]")]
318 protected float m_fFullRegenerationTime;
335 super.OnDamage(damageContext);
340 RemovePassiveRegeneration();
341 ScheduleRegeneration();
345 override void OnHealthSet()
352 RemovePassiveRegeneration();
353 ScheduleRegeneration();
358 float CalculatePassiveRegeneration()
360 if (m_fFullRegenerationTime <= 0)
364 array<EDamageType> damageTypes = {};
371 if (GetDamageOverTime(damageType) != 0)
380 void ScheduleRegeneration()
382 GetGame().GetCallqueue().Remove(UpdatePassiveRegeneration);
383 GetGame().GetCallqueue().CallLater(UpdatePassiveRegeneration, 1000 * m_fRegenerationDelay,
true);
388 void RemovePassiveRegeneration()
390 GetGame().GetCallqueue().Remove(UpdatePassiveRegeneration);
397 protected void UpdatePassiveRegeneration()
399 float regenerationRate = CalculatePassiveRegeneration();
400 SetDamageOverTime(
EDamageType.REGENERATION, regenerationRate);
404 override void OnDamageStateChanged()
406 super.OnDamageStateChanged();
409 RemovePassiveRegeneration();
418 override float ComputeEffectiveDamage(notnull
BaseDamageContext damageContext,
bool isDOT)
421 if (damageContext.damageType ==
EDamageType.COLLISION && damageContext.struckHitZone ==
this)
425 damageManager.HandleFallDamage(damageContext.damageValue);
430 return super.ComputeEffectiveDamage(damageContext, isDOT);
441 super.OnDamageStateChanged();
451 damageManager.UpdateConsciousness();
457 float regeneration = super.CalculatePassiveRegeneration();
463 regeneration *= manager.GetResilienceRegenScale();
469 override void OnInit(IEntity pOwnerEntity, GenericComponent pManagerComponent)
471 super.OnInit(pOwnerEntity, pManagerComponent);
473 if (characterDamageManager)
474 characterDamageManager.SetResilienceHitZone(
this);
482 ref map<SCR_CharacterHitZone, ref SCR_BleedingHitZoneParameters> m_mHitZoneDOTMap;
483 ref array<float> m_aHitZoneGroupBleedings;
486 override void OnDamageStateChanged()
488 super.OnDamageStateChanged();
490 UpdateConsciousness();
494 void UpdateConsciousness()
498 damageManager.UpdateConsciousness();
502 override void OnInit(IEntity pOwnerEntity, GenericComponent pManagerComponent)
504 super.OnInit(pOwnerEntity, pManagerComponent);
508 manager.SetBloodHitZone(
this);
514 if (!m_mHitZoneDOTMap)
515 m_mHitZoneDOTMap =
new map<SCR_CharacterHitZone, ref SCR_BleedingHitZoneParameters>();
517 if (m_mHitZoneDOTMap.Contains(hitZone))
519 m_mHitZoneDOTMap.Set(hitZone, localBleedingHZParams);
523 return m_mHitZoneDOTMap.Insert(hitZone, localBleedingHZParams);
529 if (!m_mHitZoneDOTMap)
532 m_mHitZoneDOTMap.Remove(hitZone);
534 if (m_mHitZoneDOTMap.IsEmpty())
535 m_mHitZoneDOTMap =
null;
541 if (!m_mHitZoneDOTMap || m_mHitZoneDOTMap.IsEmpty())
546 if (m_mHitZoneDOTMap.Find(hitZone, localBleedingHZParams))
547 return localBleedingHZParams.m_fDamageOverTime;
553 map<SCR_CharacterHitZone, ref SCR_BleedingHitZoneParameters> GetHitZoneDOTMap()
555 return m_mHitZoneDOTMap;
563 override void OnInit(IEntity pOwnerEntity, GenericComponent pManagerComponent)
565 super.OnInit(pOwnerEntity, pManagerComponent);
569 manager.SetHeadHitZone(
this);
573 override void OnDamageStateChanged()
575 super.OnDamageStateChanged();
588 manager.SoundKnockout();