/atom ///any atom that uses integrity and can be damaged must set this to true, otherwise the integrity procs will throw an error var/uses_integrity = FALSE VAR_PROTECTED/datum/armor/armor_type = /datum/armor/none VAR_PRIVATE/datum/armor/armor VAR_PRIVATE/atom_integrity //defaults to max_integrity var/max_integrity = 500 var/integrity_failure = 0 //0 if we have no special broken behavior, otherwise is a percentage of at what point the atom breaks. 0.5 being 50% ///Damage under this value will be completely ignored var/damage_deflection = 0 var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF /// The essential proc to call when an atom must receive damage of any kind. /atom/proc/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) if(!uses_integrity) CRASH("[src] had /atom/proc/take_damage() called on it without it being a type that has uses_integrity = TRUE!") if(QDELETED(src)) CRASH("[src] taking damage after deletion") if(atom_integrity <= 0) CRASH("[src] taking damage while having <= 0 integrity") if(sound_effect) play_attack_sound(damage_amount, damage_type, damage_flag) if(resistance_flags & INDESTRUCTIBLE) return damage_amount = run_atom_armor(damage_amount, damage_type, damage_flag, attack_dir, armour_penetration) if(damage_amount < DAMAGE_PRECISION) return if(SEND_SIGNAL(src, COMSIG_ATOM_TAKE_DAMAGE, damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration) & COMPONENT_NO_TAKE_DAMAGE) return . = damage_amount var/previous_atom_integrity = atom_integrity update_integrity(atom_integrity - damage_amount) var/integrity_failure_amount = integrity_failure * max_integrity //BREAKING FIRST if(integrity_failure && previous_atom_integrity > integrity_failure_amount && atom_integrity <= integrity_failure_amount) atom_break(damage_flag) //DESTROYING SECOND if(atom_integrity <= 0 && previous_atom_integrity > 0) atom_destruction(damage_flag) /// Proc for recovering atom_integrity. Returns the amount repaired by /atom/proc/repair_damage(amount) if(amount <= 0) // We only recover here return var/new_integrity = min(max_integrity, atom_integrity + amount) . = new_integrity - atom_integrity update_integrity(new_integrity) if(integrity_failure && atom_integrity > integrity_failure * max_integrity) atom_fix() /// Handles the integrity of an atom changing. This must be called instead of changing integrity directly. /atom/proc/update_integrity(new_value) SHOULD_NOT_OVERRIDE(TRUE) if(!uses_integrity) CRASH("/atom/proc/update_integrity() was called on [src] when it doesn't use integrity!") var/old_value = atom_integrity new_value = max(0, new_value) if(atom_integrity == new_value) return atom_integrity = new_value on_update_integrity(old_value, new_value) return new_value /// Handle updates to your atom's integrity /atom/proc/on_update_integrity(old_value, new_value) SHOULD_NOT_SLEEP(TRUE) SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ATOM_INTEGRITY_CHANGED, old_value, new_value) /// This mostly exists to keep atom_integrity private. Might be useful in the future. /atom/proc/get_integrity() SHOULD_BE_PURE(TRUE) return atom_integrity /// Similar to get_integrity, but returns the percentage as [0-1] instead. /atom/proc/get_integrity_percentage() SHOULD_BE_PURE(TRUE) return round(atom_integrity / max_integrity, 0.01) ///returns the damage value of the attack after processing the atom's various armor protections /atom/proc/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir, armour_penetration = 0) if(!uses_integrity) CRASH("/atom/proc/run_atom_armor was called on [src] without being implemented as a type that uses integrity!") if(damage_flag == MELEE && damage_amount < damage_deflection) return 0 if(damage_type != BRUTE && damage_type != BURN) return 0 var/armor_protection = 0 if(damage_flag) armor_protection = get_armor_rating(damage_flag) if(armor_protection) //Only apply weak-against-armor/hollowpoint effects if there actually IS armor. armor_protection = clamp(PENETRATE_ARMOUR(armor_protection, armour_penetration), min(armor_protection, 0), 100) return round(damage_amount * (100 - armor_protection) * 0.01, DAMAGE_PRECISION) ///the sound played when the atom is damaged. /atom/proc/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) switch(damage_type) if(BRUTE) if(damage_amount) playsound(src, 'sound/items/weapons/smash.ogg', 50, TRUE) else playsound(src, 'sound/items/weapons/tap.ogg', 50, TRUE) if(BURN) playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE) ///Called to get the damage that hulks will deal to the atom. /atom/proc/hulk_damage() return 150 //the damage hulks do on punches to this atom, is affected by melee armor /atom/proc/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0) //used by attack_alien, attack_animal if(!uses_integrity) CRASH("unimplemented /atom/proc/attack_generic()!") user.do_attack_animation(src) user.changeNext_move(CLICK_CD_MELEE) return take_damage(damage_amount, damage_type, damage_flag, sound_effect, get_dir(src, user), armor_penetration) /// Called after the atom takes damage and integrity is below integrity_failure level /atom/proc/atom_break(damage_flag) SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ATOM_BREAK, damage_flag) /// Called when integrity is repaired above the breaking point having been broken before /atom/proc/atom_fix() SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ATOM_FIX) ///what happens when the atom's integrity reaches zero. /atom/proc/atom_destruction(damage_flag) SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ATOM_DESTRUCTION, damage_flag) ///changes max_integrity while retaining current health percentage, returns TRUE if the atom got broken. /atom/proc/modify_max_integrity(new_max, can_break = TRUE, damage_type = BRUTE) if(!uses_integrity) CRASH("/atom/proc/modify_max_integrity() was called on [src] when it doesn't use integrity!") var/current_integrity = atom_integrity var/current_max = max_integrity if(current_integrity != 0 && current_max != 0) var/percentage = current_integrity / current_max current_integrity = max(1, round(percentage * new_max)) //don't destroy it as a result atom_integrity = current_integrity max_integrity = new_max if(can_break && integrity_failure && current_integrity <= integrity_failure * max_integrity) atom_break(damage_type) return TRUE return FALSE /// A cut-out proc for [/atom/proc/bullet_act] so living mobs can have their own armor behavior checks without causing issues with needing their own on_hit call /atom/proc/check_projectile_armor(def_zone, obj/projectile/impacting_projectile, is_silent) if(uses_integrity) return clamp(PENETRATE_ARMOUR(get_armor_rating(impacting_projectile.armor_flag), impacting_projectile.armour_penetration), 0, 100) return 0