Adds an universal object health system. (#21266)

https://www.youtube.com/watch?v=ZpUYjpKg9KY

As per title. The goal is to add health to most objects in the game and
thus allow them to be dynamically destructible, while also avoiding
shitcode like each structure having its own snowflake health update
proc.

---------

Signed-off-by: Matt Atlas <mattiathebest2000@hotmail.it>
Co-authored-by: Matt Atlas <liermattia@gmail.com>
Co-authored-by: SleepyGemmy <99297919+SleepyGemmy@users.noreply.github.com>
This commit is contained in:
Matt Atlas
2026-04-16 23:22:08 +00:00
committed by GitHub
co-authored by Matt Atlas SleepyGemmy
parent 1d2871729f
commit d5931c5ffe
213 changed files with 1448 additions and 1424 deletions
+33 -34
View File
@@ -12,8 +12,8 @@
layer = BLOB_SHIELD_LAYER
var/maxHealth = 30
var/health
maxhealth = 30
var/regen_rate = 5
/// Damage gets divided by these modifiers, based on damage type
@@ -40,7 +40,6 @@
/obj/effect/blob/Initialize()
. = ..()
health = maxHealth
update_icon()
START_PROCESSING(SSprocessing, src)
@@ -54,37 +53,37 @@
/obj/effect/blob/ex_act(var/severity)
switch(severity)
if(1)
take_damage(rand(100, 120) / brute_resist)
add_damage(rand(100, 120) / brute_resist)
if(2)
take_damage(rand(60, 100) / brute_resist)
add_damage(rand(60, 100) / brute_resist)
if(3)
take_damage(rand(20, 60) / brute_resist)
add_damage(rand(20, 60) / brute_resist)
/obj/effect/blob/update_icon()
if(health > maxHealth / 2)
if(health > maxhealth / 2)
icon_state = "blob"
else
icon_state = "blob_damaged"
/obj/effect/blob/process()
if(!parent_core || QDELETED(parent_core))
take_damage(maxHealth / 4) // four processes to die if main core is deddo
add_damage(maxhealth / 4) // four processes to die if main core is deddo
return
regen()
if(world.time < (attack_time + attack_cooldown))
return
attempt_attack()
/obj/effect/blob/proc/take_damage(var/damage)
health -= damage
if(health < 0)
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, TRUE)
qdel(src)
else
update_icon()
/obj/effect/blob/add_damage(damage, damage_flags, damage_type, armor_penetration, obj/weapon)
. = ..()
update_icon()
/obj/effect/blob/on_death(damage, damage_flags, damage_type, armor_penetration, obj/weapon)
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, TRUE)
. = ..()
/obj/effect/blob/proc/regen()
health = min(health + regen_rate, maxHealth)
health = min(health + regen_rate, maxhealth)
update_icon()
/obj/effect/blob/proc/expand(var/turf/T)
@@ -92,11 +91,11 @@
return
if(istype(T, /turf/simulated/wall))
var/turf/simulated/wall/SW = T
SW.take_damage(80)
SW.add_damage(80)
return
var/obj/structure/girder/G = locate() in T
if(G)
G.take_damage(rand(40, 80))
G.add_damage(rand(40, 80))
return
var/obj/structure/window/W = locate() in T
if(W)
@@ -108,7 +107,7 @@
return
var/obj/structure/tank_wall/TW = locate() in T
if(TW)
TW.take_damage(rand(5,20))
TW.add_damage(rand(5,20))
return
for(var/obj/machinery/door/D in T) // There can be several - and some of them can be open, locate() is not suitable
if(D.density)
@@ -139,7 +138,7 @@
return
var/obj/machinery/camera/CA = locate() in T
if(CA && !(CA.stat & BROKEN))
CA.take_damage(30)
CA.add_damage(30)
return
// Above things, we destroy completely and thus can use locate. Mobs are different.
@@ -180,7 +179,7 @@
if(!D)
return
attack_msg(D)
D.take_damage(rand(damage_min, damage_max))
D.add_damage(rand(damage_min, damage_max))
/obj/effect/blob/proc/attack_living(var/mob/living/L)
if(!L)
@@ -207,9 +206,9 @@
switch(hitting_projectile.damage_type)
if(DAMAGE_BRUTE)
take_damage(hitting_projectile.damage / brute_resist)
add_damage(hitting_projectile.damage / brute_resist)
if(DAMAGE_BURN)
take_damage((hitting_projectile.damage / laser_resist) / fire_resist)
add_damage((hitting_projectile.damage / laser_resist) / fire_resist)
/obj/effect/blob/attackby(obj/item/attacking_item, mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -235,12 +234,12 @@
if(DAMAGE_BRUTE)
damage = (attacking_item.force / brute_resist)
take_damage(damage)
add_damage(damage)
/obj/effect/blob/fire_act(exposed_temperature, exposed_volume)
. = ..()
take_damage(rand(5, 20) / fire_resist)
add_damage(rand(5, 20) / fire_resist)
#define CORE_SHIELD_HIGH "high"
#define CORE_SHIELD_LOW "low"
@@ -249,7 +248,7 @@
name = "master nucleus"
desc = "A massive, fragile nucleus guarded by a shield of thick tendrils."
icon_state = "blob_core"
maxHealth = 450
maxhealth = 450
damage_min = 25
damage_max = 35
expandType = /obj/effect/blob/shield
@@ -265,7 +264,7 @@
var/times_to_pulse = 4
/obj/effect/blob/core/proc/get_health_percent()
return ((health / maxHealth) * 100)
return ((health / maxhealth) * 100)
/obj/effect/blob/core/proc/process_core_health()
var/health_percent = get_health_percent()
@@ -313,7 +312,7 @@
name = "auxiliary nucleus"
desc = "An interwoven mass of tendrils. A glowing nucleus pulses at its center."
icon_state = "blob_node"
maxHealth = 125
maxhealth = 125
regen_rate = 1
damage_min = 15
damage_max = 20
@@ -324,7 +323,7 @@
/obj/effect/blob/core/secondary/process()
if(!parent_core || QDELETED(parent_core))
take_damage(maxHealth / 4) // four processes to die if main core is deddo
add_damage(maxhealth / 4) // four processes to die if main core is deddo
return
..()
@@ -332,13 +331,13 @@
return
/obj/effect/blob/core/secondary/update_icon()
icon_state = (health / maxHealth >= 0.5) ? "blob_node" : "blob_factory"
icon_state = (health / maxhealth >= 0.5) ? "blob_node" : "blob_factory"
/obj/effect/blob/shield
name = "shielding mass"
desc = "A pulsating mass of interwoven tendrils. These seem particularly robust, but not quite as active."
icon_state = "blob_idle"
maxHealth = 120
maxhealth = 120
damage_min = 15
damage_max = 25
attack_cooldown = 45
@@ -357,9 +356,9 @@
return ..()
/obj/effect/blob/shield/update_icon()
if(health > maxHealth * 2 / 3)
if(health > maxhealth * 2 / 3)
icon_state = "blob_idle"
else if(health > maxHealth / 3)
else if(health > maxhealth / 3)
icon_state = "blob"
else
icon_state = "blob_damaged"
@@ -372,7 +371,7 @@
/obj/effect/blob/ravaging
name = "ravaging mass"
desc = "A mass of interwoven tendrils. They thrash around haphazardly at anything in reach."
maxHealth = 20
maxhealth = 20
damage_min = 20
damage_max = 25
attack_cooldown = 30