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
+37 -88
View File
@@ -17,11 +17,11 @@
anchored = TRUE
atom_flags = ATOM_FLAG_CHECKS_BORDER
obj_flags = OBJ_FLAG_ROTATABLE|OBJ_FLAG_MOVES_UNSUPPORTED
var/hitsound = 'sound/effects/glass_hit.ogg'
var/maxhealth = 14
maxhealth = OBJECT_HEALTH_FRAGILE
hitsound = 'sound/effects/glass_hit.ogg'
var/maximal_heat = T0C + 100 // Maximal heat before this window begins taking damage from fire
var/damage_per_fire_tick = 2 // Amount of damage per fire tick. Regular windows are not fireproof so they might as well break quickly.
var/health
var/ini_dir = null
var/state = 2
var/reinf = FALSE
@@ -34,27 +34,26 @@
atmos_canpass = CANPASS_PROC
/obj/structure/window/condition_hints(mob/user, distance, is_adjacent)
. += ..()
/obj/structure/window/get_damage_condition_hints(mob/user, distance, is_adjacent)
if(health == maxhealth)
. += SPAN_NOTICE("It looks fully intact.")
. = SPAN_NOTICE("It looks fully intact.")
else
var/perc = health / maxhealth
if(perc > 0.75)
. += SPAN_NOTICE("It has a few cracks.")
. = SPAN_NOTICE("It has a few cracks.")
else if(perc > 0.5)
. += SPAN_WARNING("It looks slightly damaged.")
. = SPAN_WARNING("It looks slightly damaged.")
else if(perc > 0.25)
. += SPAN_WARNING("It looks moderately damaged.")
. = SPAN_WARNING("It looks moderately damaged.")
else
. += SPAN_DANGER("It looks heavily damaged.")
. = SPAN_DANGER("It looks heavily damaged.")
if(silicate)
if (silicate < 30)
. += SPAN_NOTICE("It has a thin layer of silicate.")
. = SPAN_NOTICE("It has a thin layer of silicate.")
else if (silicate < 70)
. += SPAN_NOTICE("It is covered in silicate.")
. = SPAN_NOTICE("It is covered in silicate.")
else
. += SPAN_NOTICE("There is a thick layer of silicate covering it.")
. = SPAN_NOTICE("There is a thick layer of silicate covering it.")
/obj/structure/window/proc/update_nearby_icons()
QUEUE_SMOOTH_NEIGHBORS(src)
@@ -65,38 +64,14 @@
layer = ABOVE_HUMAN_LAYER
QUEUE_SMOOTH(src)
/obj/structure/window/proc/take_damage(var/damage = 0, var/sound_effect = 1, message = TRUE)
var/initialhealth = health
if(silicate)
damage = damage * (1 - silicate / 200)
health = max(0, health - damage)
if(health <= 0)
shatter(message)
else
if(sound_effect)
playsound(loc, 'sound/effects/glass_hit.ogg', 100, 1)
if(health < maxhealth / 4 && initialhealth >= maxhealth / 4)
if(message)
visible_message(SPAN_DANGER("[src] looks like it's about to shatter!"))
playsound(loc, SFX_GLASS_CRACK, 100, 1)
else if(health < maxhealth / 2 && initialhealth >= maxhealth / 2)
if(message)
visible_message(SPAN_WARNING("[src] looks seriously damaged!"))
playsound(loc, SFX_GLASS_CRACK, 100, 1)
else if(health < maxhealth * 3/4 && initialhealth >= maxhealth * 3/4)
if(message)
visible_message(SPAN_WARNING("Cracks begin to appear in [src]!"))
playsound(loc, SFX_GLASS_CRACK, 100, 1)
return
/obj/structure/window/on_death(damage, damage_flags, damage_type, armor_penetration, obj/weapon, message)
shatter(message)
/obj/structure/window/proc/apply_silicate(var/amount)
if(health < maxhealth) // Mend the damage.
health = min(health + amount * 3, maxhealth)
add_health(amount * 3)
if(health == maxhealth)
visible_message("[src] looks fully repaired." )
visible_message(SPAN_NOTICE("The silicate fully mends the damage on \the [src]."))
else // Reinforce.
silicate = min(silicate + amount, 100)
updateSilicate()
@@ -145,23 +120,19 @@
if(. != BULLET_ACT_HIT)
return .
take_damage(proj_damage)
return
add_damage(proj_damage)
/obj/structure/window/ex_act(severity)
switch(severity)
if(1)
qdel(src)
return
if(2)
shatter(0)
return
if(3)
if(prob(50))
shatter(0)
return
else
take_damage(rand(10,30), TRUE, FALSE)
add_damage(rand(10,30), TRUE, FALSE)
// This and relevant verbs/procs can probably be removed since full windows are a thing now. -Gem
/obj/structure/window/proc/is_full_window()
@@ -210,7 +181,7 @@
anchored = 0
update_nearby_icons()
step(src, get_dir(hitting_atom, src))
take_damage(tforce)
add_damage(tforce)
/obj/structure/window/attack_hand(var/mob/living/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -243,7 +214,7 @@
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(damage >= 10)
visible_message(SPAN_DANGER("[user] smashes into [src]!"))
take_damage(damage)
add_damage(damage)
else
visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly."))
playsound(src.loc, 'sound/effects/glass_hit.ogg', 10, 1, -2)
@@ -347,8 +318,7 @@
/obj/structure/window/proc/hit(var/damage, var/sound_effect = 1)
if(reinf)
damage *= 0.5
take_damage(damage)
return
add_damage(damage)
/obj/structure/window/proc/dismantle_window()
if(dir == SOUTHWEST)
@@ -369,8 +339,6 @@
if (start_dir)
set_dir(start_dir)
health = maxhealth
ini_dir = dir
update_nearby_tiles(need_rebuild=1)
@@ -429,7 +397,7 @@
glasstype = /obj/item/stack/material/glass
maximal_heat = T0C + 100
damage_per_fire_tick = 2
maxhealth = 12
maxhealth = OBJECT_HEALTH_FRAGILE
/obj/structure/window/basic/full
name = "glass"
@@ -441,7 +409,7 @@
desc = "It looks rather strong. Might take a few good hits to shatter it."
icon_state = "rwindow"
basestate = "rwindow"
maxhealth = 40
maxhealth = OBJECT_HEALTH_LOW
reinf = TRUE
maximal_heat = T0C + 750
damage_per_fire_tick = 2
@@ -462,7 +430,7 @@
/obj/structure/window/reinforced/tinted/frosted
name = "reinforced frosted glass pane"
desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window."
maxhealth = 30
maxhealth = OBJECT_HEALTH_EXTREMELY_LOW
/obj/structure/window/reinforced/polarized
name = "reinforced electrochromic glass pane"
@@ -489,9 +457,6 @@
/obj/structure/window/reinforced/crescent/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return
/obj/structure/window/reinforced/crescent/take_damage()
return
/obj/structure/window/reinforced/crescent/shatter()
return
@@ -536,7 +501,7 @@
glasstype = /obj/item/stack/material/glass/phoronglass
maximal_heat = T0C + 2000
damage_per_fire_tick = 1 // This should last for 80 fire ticks if the window is not damaged at all. The idea is that borosilicate windows have something like ablative layer that protects them for a while.
maxhealth = 40
maxhealth = OBJECT_HEALTH_VERY_LOW
/// Phoron-infused silicate
rad_resistance_modifier = 4
@@ -547,13 +512,13 @@
glasstype = /obj/item/stack/material/glass/phoronrglass
reinf = TRUE
maximal_heat = T0C + 4000
maxhealth = 80
maxhealth = OBJECT_HEALTH_LOW
/obj/structure/window/borosilicate/reinforced/skrell
name = "advanced borosilicate alloy window"
desc = "A window made out of a higly advanced borosilicate alloy. It seems to be extremely strong."
color = GLASS_COLOR_PHORON
maxhealth = 250
maxhealth = OBJECT_HEALTH_HIGH
/********** Shuttle Windows **********/
/obj/structure/window/shuttle
@@ -564,7 +529,7 @@
basestate = "w"
atom_flags = 0
obj_flags = null
maxhealth = 40
maxhealth = OBJECT_HEALTH_VERY_LOW
reinf = TRUE
dir = 5
smoothing_flags = SMOOTH_TRUE
@@ -579,8 +544,7 @@
/obj/structure/window/shuttle/legion
name = "reinforced cockpit window"
icon = 'icons/obj/smooth/shuttle_window_legion.dmi'
health = 160
maxhealth = 160
maxhealth = OBJECT_HEALTH_HIGH
/obj/structure/window/shuttle/palepurple
icon = 'icons/obj/smooth/shuttle_window_palepurple.dmi'
@@ -589,8 +553,7 @@
name = "advanced borosilicate alloy window"
desc = "It looks extremely strong. Might take many good hits to crack it."
icon = 'icons/obj/smooth/skrell_window_purple.dmi'
health = 500
maxhealth = 500
maxhealth = OBJECT_HEALTH_VERY_HIGH
smoothing_flags = SMOOTH_MORE | SMOOTH_DIAGONAL
canSmoothWith = list(
/turf/simulated/wall/shuttle/skrell,
@@ -602,8 +565,7 @@
desc = "It looks extremely strong. Might take many good hits to crack it."
icon = 'icons/turf/smooth/scc_ship/scc_ship_windows.dmi'
icon_state = "map_window"
health = 500
maxhealth = 500
maxhealth = OBJECT_HEALTH_VERY_HIGH
alpha = 255
smoothing_flags = SMOOTH_MORE
canSmoothWith = list(
@@ -629,15 +591,11 @@
/obj/structure/window/shuttle/scc
icon = 'icons/obj/smooth/scc_shuttle_window.dmi'
health = 160
maxhealth = 160
maxhealth = OBJECT_HEALTH_HIGH
/obj/structure/window/shuttle/crescent
desc = "It looks rather strong."
/obj/structure/window/shuttle/crescent/take_damage()
return
//
// Full Windows
//
@@ -647,7 +605,7 @@
atom_flags = 0
obj_flags = null
dir = 5
maxhealth = 28 // Two glass panes worth of health, since that's the minimum you need to break through to get to the other side.
maxhealth = OBJECT_HEALTH_EXTREMELY_LOW
glasstype = /obj/item/stack/material/glass
shardtype = /obj/item/material/shard
full = TRUE
@@ -757,14 +715,12 @@
qdel(src)
return
/obj/structure/window/full/take_damage(var/damage = 0, var/sound_effect = 1)
/obj/structure/window/full/add_damage(damage, damage_flags, damage_type, armor_penetration, obj/weapon, sound_effect = TRUE)
var/initialhealth = health
if(silicate)
damage = damage * (1 - silicate / 200)
health = max(0, health - damage)
if(health <= 0)
shatter()
else
@@ -779,7 +735,6 @@
else if(health < maxhealth * 3/4 && initialhealth >= maxhealth * 3/4)
visible_message(SPAN_WARNING("Cracks begin to appear in [src]!"))
playsound(loc, SFX_GLASS_CRACK, 100, 1)
return
/obj/structure/window/full/dismantle_window()
var/obj/item/stack/material/mats = new glasstype(loc)
@@ -803,7 +758,7 @@
icon = 'icons/obj/smooth/window/full_window.dmi'
icon_state = "window_glass"
basestate = "window_glass"
maxhealth = 80 // Two reinforced panes worth of health, since that's the minimum you need to break through to get to the other side.
maxhealth = OBJECT_HEALTH_LOW
reinf = TRUE
maximal_heat = T0C + 750
glasstype = /obj/item/stack/material/glass/reinforced
@@ -834,9 +789,6 @@
/obj/structure/window/full/reinforced/indestructible/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return
/obj/structure/window/full/reinforced/indestructible/take_damage()
return
/obj/structure/window/full/reinforced/indestructible/shatter()
return
@@ -867,9 +819,6 @@
/obj/structure/window/full/reinforced/polarized/indestructible/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return
/obj/structure/window/full/reinforced/polarized/indestructible/take_damage()
return
/obj/structure/window/full/reinforced/polarized/indestructible/shatter()
return
@@ -898,7 +847,7 @@
basestate = "window_glass"
glasstype = /obj/item/stack/material/glass/phoronglass
shardtype = /obj/item/material/shard/phoron
maxhealth = 80 // Two borosilicate glass panes worth of health, since that's the minimum you need to break through to get to the other side.
maxhealth = OBJECT_HEALTH_LOW
maximal_heat = T0C + 2000
damage_per_fire_tick = 1
@@ -907,7 +856,7 @@
name = "reinforced borosilicate window"
desc = "A borosilicate alloy window, with rods supporting it. It seems to be very strong."
glasstype = /obj/item/stack/material/glass/phoronrglass
maxhealth = 160 // Two reinforced borosilicate glass panes worth of health, since that's the minimum you need to break through to get to the other side.
maxhealth = OBJECT_HEALTH_MEDIUM
reinf = TRUE
maximal_heat = T0C + 4000
rad_resistance_modifier = 4