Damage System – Arma Reforger
The Damage System is the system that handle entity damage.
Logic Flow
The workflows goes as follows (see the attached image):
- damage is received
- the DamageManagerComponent checks that:
- damage handling is enabled
- damage handling is not hijacked
- received damage should count as a hit
- check if the entity is dead
- check if actual damage shalt be dealt
- it then
- sends damage to the hit zone which calculates the effective damage
- replicate the hit over the network for clients to calculate damage locally and display damage effects
- deals damage
- sends damage to the hit zone again for it to
- deal damage
- trigger OnDamage()
- pass damage to the parent hit zones and do the same process for themselves
- triggers DamageManager's OnDamage()
- if the DamageStates did not change, exits
- if they did, trigger the hit zone's OnDamageStateChanged() then its own OnDamageStateChanged()
Classes
Class hierarchy goes as follows:
ExtendedDamageManagerComponent Changes
The following API will not be useful for damageManagers inheriting from ExtendedDamageManagerComponent:
DamageEffects logic replaces the conventional damageOverTime, and has its own API.
OnDamageOverTimeAdded() |
OnDamageEffectAdded() |
OnDamageOverTimeRemoved() |
OnDamageEffectRemoved() |
IsDamagedOverTime() |
SCR_CharacterDamageManagerComponent.IsBleeding()
GetPersistentEffects()
GetAllPersistentEffectsOnHitZone()
GetAllPersistentEffectsOfType() |
GetDamageOverTime() |
SCR_RegeneratingHitZone.GetHitZoneDamageOverTime()
SCR_CharacterBloodHitZone.GetTotalBleedingAmount() |
SetHealth()
The HitZone.SetHealth() method "magically" changes the amount of HP a hit zone has.
This means that the flowchart above will not take place since damage is not actually being dealt - this also means there is no instigator. Instead, only OnHealthSet() and OnDamageStateChanged() are called.
SetHealth() should be used very sparingly, for example in cases where one wants to force a character death, even when damage handling is disabled for that manager (remember, this method is not dealing damage, the health is magically changing).
An alternative to SetHealth() is to call HandleDamage and deal the entity's max health as true damage. By doing it this way, the entity is guaranteed to take enough damage to be destroyed, and the callback structure of a more "natural" cause of death above is maintained.
Bad Usage Example
Keep in mind that doing HandleDamage with true damage max health is not always the best way to destroy an entity. For example, killing a player inside a vehicle.
At first glance, killing the player by dealing damage equal to their max hp sounds like a good idea (and it would work fine on vanilla). However, now think of the (non-existent) Terminator mod.
If the Terminator is inside of a vehicle and the vehicle explodes, it should survive it (because of small damage multipliers, a large health pool, etc.), but since the applied damage is equal to max health, it would die.
This is why the best way to destroy entities is by dealing a "realistic" amount of damage that achieves the wanted goal, but that could technically be survivable by a modded entity.
When writing damage code, try to think of this fictional Terminator mod to ensure as much compatibility as possible.