Skip to content

Standardized Health

Mucker edited this page May 29, 2022 · 4 revisions

Standardized Health

The standardized health system is a collection of procs and vars designed to simplify and standardize the processing of health and damage throughout the codebase. SierraKomodo designed this system to counter the pre-existing issues created by the varying implementations of health processing found throughout the codebase. These issues included inconsistent damage ratios, resistances, and in some cases, a complete inability to handle health at all for some damage sources such as explosions, fires, EMPs, or projectiles.

Reviewers will request any new form of health handling to use the standardized health system before approving unless the PR author provides a good reason not to use it.

Health damage is currently processed automatically for the following proc chains:

  • attackby()
  • bullet_act()
  • emp_act()
  • ex_act()
  • fire_act()

See code\game\atoms_health.dm for additional specific details on each var and proc - This file documents every var and proc with its intended use and function. You can also find this information on the auto-generated DMDoc document for /atom here, though DMDoc combines these with all defined vars and procs under that scope, including non-health related items.

Note that all values passed through standardized health are rounded to the nearest whole. The system does not support decimals or floating-point integers.

Simplified Implementation

The most straightforward implementations of standardized health need only to do two things.

  1. Assign a value to health_max in the atom's initial definition.
  2. Create an override for on_death() to handle the atom dying. I.e., if it should qdel() itself, stop processing, display a message or play a sound, run update_icon(), etc.

The system will handle everything else from there, including initializing current health, setting default resistance values, and handling any damage.

Here's an example from girders. Non health-related vars and procs are hidden here for example purposes:

/obj/structure/girder
	health_max = 100

/obj/structure/girder/on_death()
	dismantle()

This code grants girders 100 HP when they initialize and runs the dismantle() proc when they "die" (Their HP hits 0).

Variables

This section lists every var provided by the standardized health system, their intended use, and some example uses. You can also find documentation on each var in the code\game\atoms_health.dm file.

Note that these are listed in alphabetical order and not the order in which they appear in the file.

Damage Hit Sound - var/damage_hitsound

This var is a sound file that should play whenever the atom is hit and takes damage. The following proc chains play this sound file:

  • attackby()
  • bullet_act()

This is not used for emp_act(), fire_act(), or ex_act() by default to avoid flooding clients with sound calls during those events.

Current Health - var/health_current

This var holds the atom's current health value and is always an integer. Initialization sets this to the value of health_max.

You should NEVER modify this var directly and do not define it in your atom. Instead, use one of the following procs depending on your intent:

  • To damage the atom: damage_health()
  • To heal the atom: restore_health()
  • To kill the atom: kill_health()
  • To revive or fully heal the atom: revive_health()
  • To set a specific health value that isn't covered by any of the above: set_health()

If you wish to reference this to determine the atom's current health value, it is recommended to instead use get_current_health() unless you know this reference will never need to account for any additional checks certain atoms may make toward determine it's actual health value (I.e., wall rot on walls).

Death Status - var/health_dead

This var is a boolean that serves as a quick method of checking whether or not the atom is 'dead'. You can freely reference this variable for checks, but do not modify it directly. The system expects this variable to remain in sync with the actual health values.

If you wish to directly change an atom's death status, use one of these procs instead:

  • To kill an atom: kill_health()
  • To revive a 'dead' atom: revive_health()

Maximum Health - var/health_max

This var holds the atom's maximum health value. Generally, this is defined in the atom's original path and never touched again. This value must be an integer.

The health system will ensure health_current never exceeds this value, though the result of get_current_health() may legally exceed it depending on implementations.

You should never modify this directly outside of defining it in your atom's path. Instead, use set_max_health().

It is also recommended to use get_max_health() instead of referencing this var directly, as some atoms may have additional handling or overrides that may change the perceived maximum health.

Minimum Damage Threshhold - var/health_min_damage

This var serves as a rudimentary 'armor' system by defining a minimum damage value that must be passed in order for the atom to actually be damaged. I.e., if this is set to 50, then any damage_health() calls whos damage falls below this value will be skipped.

This primarily exists to prevent low-damage random items from being able to eventually break something that should be sturdy. Examples include walls and reinforced windows being immune to damage from a pair of shoes or a broken light bulb.

Health Resistances - var/list/health_resistances

This var contains a LAZYLIST of all damage types and the atom's resistance value against those types. This list is formatted as:

list(
	DAMAGE_TYPE = RESISTANCE_VALUE
)

Where DAMAGE_TYPE must be one of the DAMAGE_* type defines, and RESISTANCE_VALUE is a positive or zero floating point integer. The resistance value is the multiplier applied to any incoming damage of the given type. A value of 1 is neither a resistance nor a weakness. A value above 1 is a weakness. A value below 1 is a resistance. A value of 0 is treated as an immunity and also causes can_damage_health() checks for the given type to fail.

See code\__defines\health.dm for a list of all defined damage types, and some preset damage resistances that may be useful.

You should not modify or reference this var directly outside of defining it in the atom's initial path. Instead, use the following procs:

  • Retrieve the atom's resistance to a given damage type: get_damage_resistance()
  • Set an atom's resistance to a given damage type: set_damage_resistance()
  • Remove an atom's resistance to a given damage type:
  • remove_damage_resistance()

Procs

This section lists every proc provided by the standardized health system, their intended use, and some example uses. You can also find documentation on each proc in the code\game\atoms_health.dm file.

Note, these are listed in alphabetical order and not the order in which they appear in the file.

Can Damage Health - `proc/can_damage_health(damage, damage_type)

This checks whether or not the atom can be damaged with the given damage value and type. By default, this check fails on the following conditions:

  • damage is <= 0
  • The atom is already dead (health_dead is set)
  • damage is less than health_min_damage
  • The atom has an immunity to damage_type (The health_resistances value for this type is 0)
  • For mobs, if the mob has the GODMODE flag enabled

This proc is automatically checked in damage_health() before applying any damage.

Can Restore Health - `proc/can_restore_health(damage, damage_type)

This checks whether or not the atom is capable of having it's health restored. By default, this check only fails if no damage is provided or if current health equals max health. This proc does not directly reference health vars.

Copy Health - proc/copy_health(source_atom, target_atom)

This is a global proc. This copies all health data from the source atom to the target atom.

Damage Health - proc/damage_health(damage, damage_type, damage_flags, severity)

This proc handles damaging the atom. It checks the result of can_damage_health() before applying damage. The damage value is multiplied by the atom's resistance to the given damage type (See health_resistances).

severity is a direct pass of the severity parameters from ex_act() and emp_act(), as applicable, and should only be present if damage_type is DAMAGE_EXPLODE or DAMAGE_EMP.

This proc calls the on_death() proc as applicable if the new health value causes the atom's death state to change, unless damage_flags includes the DAMAGE_FLAG_SKIP_DEATH_STATE_CHANGE flag.

Examine Damage State - proc/examine_damage_state(mob/user)

This proc is called during the examine() chain if the atom's health has been initialized. It is intended to provide general information on how damaged the atom looks when examined.

Default, generic overrides are already provided for atoms and mobs. Other atoms may override this proc if they wish to provide additional information or change the wording of the examine values.

Get Current Health - proc/get_current_health()

This proc is the recommended method to retrieve and reference an atom's current health in most cases. By default, it returns the value of health_current or null if health is not initialized for this atom, but may return a different value if an atom overrides this for any given reason.

WARNING: This proc may return 0 or null, both of which are falsey but have different meanings. null means the atom has no health handling initialized, while 0 means the atom's current health is considered to be 0. It is recommended to compare == 0 instead of relying on falsey/truthy values if your logic relies on the atom having initialized health.

Overrides must call the parent proc and are recommended to utilize the parent's result instead of the health_current var in its calculations.

Get Damage Percentage - proc/get_damage_ercentage(use_raw_values)

This proc takes the result of get_damage_value() and converts it to a percentage of the atom's maximum health. The result is procided as awhole number ranging from 0 to 100, where 100 is equivalent to 100%.

If use_raw_values is set, the comparison directly checks the variables instead of using the generally recommended procs.

Get Damage Resistance - proc/get_damage_resistance(damage_type)

Retrieves the atom's current resistance value to the given damage type from health_resistances. If the atom does not have an explicitly defined resistance or weakness, this returns 1 instead.

Get Damage Value - proc/get_damage_value(use_raw_values)

This proc determines the amount of damage the atom has received by subtracting current health from maximum health. If use_raw_values is set, the comparison directly checks the variables instead of using the generally recommended procs.

This proc may return a negative value. If so, this indicates the atom's perceived current health exceeds its perceived maximum health. This should never occur if using raw values, as the value of health_current is clamped between 0 and health_max.

Get Maximum Health - proc/get_max_health()

This proc is the recommended method to retrieve and reference an atom's maximum health. By default, it returns the value of health_max, but may return a different value if an atom overrides this for any given reason.

Overrides must call the parent proc and are recommended to utilize the parent's result instead of the health_max var in its calculations.

Is Health Damaged? - proc/health_damaged(use_raw_values)

This proc determines whether or not the atom has received damage, by checking if current health is less than maximum health. If use_raw_values is set, the comparison directly checks the variables instead of using the generally recommended procs.

Overrides may exist that add additional checks and conditions for whether or not an atom is considered damaged.

Kill Health - proc/kill_health()

This proc immediately kills the atom by setting its health to 0.

Modify Health - proc/mod_health(health_mod, damage_type, skip_death_state_change)

YOU SHOULD NOT USE THIS PROC DIRECTLY UNLESS YOU KNOW WHAT YOU ARE DOING. YOU PROBABLY WANT set_health(), damage_health(), OR revive_health() INSTEAD

This is an internal handler for actual health modification changes. All other procs that touch the value of health_current come here. The value of health_mod is added to the value of health_current, clamped between 0 and health_max, and then assigned back to health_current.

If this change results in health going from a positive number to 0, on_death() is called. If it goes from 0 to a positive number, on_revive() is called. These procs are skipped if skip_death_state_change is set, but health_dead is still set accordingly.

This returns a boolean value - TRUE if the death state changed, FALSE otherwise.

On Death - proc/on_death()

This proc is called by the standardized health system when the atom 'dies'. I.e., when the atom's health falls to 0. It is called after post_health_change() and after health_dead is updated.

This proc is skipped if the proc chain resulting in death set the skip_death_state_change flag.

By default, this does nothing, and exists solely for child override use.

On Revive - proc/on_revive()

This proc is called by the standardized health system when the atom 'revivews'. I.e., when the atom's health rises from 0. It is called after post_health_change() and after health_dead is updated.

This proc is skipped if the proc chain resulting in revival set the skip_death_state_change flag.

By default, this does nothing, and exists solely for child override use.

Post Health Change - proc/post_health_change(health_mod, damage_type)

This proc is called by the standardized health system after every health modification event. health_mod contains the amount the atom's health changed by, where negative numbers indicate damage and positive numbers indicate healing.

By default, this does nothing, and exists solely for child override use.

Remove Damage Resistance - proc/remove_damage_resistance(damage_type)

Removes the atom's resistance or weakness to the givevn damage type from health_resistances. This effectively sets the resistance multiplier to 1.

Restore Health - proc/restore_health(damage, damage_type, skip_death_state_change)

This proc restores the atom's health by the given amount. It checks the result of can_restore_health() before attempted to modify current health.

This proc calls the on_revive() proc as applicable if the new health value causes the atom's death state to change, unless skip_death_state_change is set.

Revive Health - proc/revive_health()

This proc fully heals the atom, including reviving it from death.

Set Current Health - proc/set_health(new_health, skip_death_state_change)

This proc force-sets the atom's current health to the new health value. This proc cannot be used to set a health value below 0, or greater than health_max.

This proc calls the on_death() or on_revive() proc as applicable if the new health value causes the atom's death state to change, unless skip_death_state_change is set.

Set Damage Resistance - proc/set_damage_resistance(damage_type, resistance_value)

This updates the atom's health_resistances to set the new resistance or weakness value for the given damage type. This value is multiplied against incoming damage in damage_health().

Values below 1 are resistance, values above 1 are weakness. A value of 0 is considered a full immunity and will cause can_damage_health() checks with the provided damage type to fail.

If the new value is 1, it calls remove_damage_resistance() instead.

Set Maximum Health - proc/set_max_health(new_max_health, set_current_health)

This proc sets a new maximum health value for the atom, and handles initializing health if it was not initialized before. If the new value is 0, this effectively disables health processing for the atom and does not call any death or revival events.

If set_current_health is set (TRUE by default), then the atom's current health is set to the new maximum health value, effectivelly fully healing the atom. Otherwise, current health is reduced to health_max if it exceeds the new maximum health value.

Documentation regarding setting up and using Git.

Making heads or tails of specific GitHub pages, for the uninitiated.

Tools

Documentation regarding tools external to DM and Git.

Documentation

Content creation guides

Standards

Standards and guidelines regarding commenting, contributor conduct and coding standards.

Clone this wiki locally