diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 30d37fb547e..adf8722b120 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -11,43 +11,6 @@ return null return format_text ? format_text(checked_area.name) : checked_area.name -/** toggle_organ_decay - * inputs: first_object (object to start with) - * outputs: - * description: A pseudo-recursive loop based off of the recursive mob check, this check looks for any organs held - * within 'first_object', toggling their frozen flag. This check excludes items held within other safe organ - * storage units, so that only the lowest level of container dictates whether we do or don't decompose - */ -/proc/toggle_organ_decay(atom/first_object) - - var/list/processing_list = list(first_object) - var/list/processed_list = list() - var/index = 1 - var/obj/item/organ/found_organ - - while(index <= length(processing_list)) - - var/atom/object_to_check = processing_list[index] - - if(isorgan(object_to_check)) - found_organ = object_to_check - found_organ.organ_flags ^= ORGAN_FROZEN - - else if(iscarbon(object_to_check)) - var/mob/living/carbon/mob_to_check = object_to_check - for(var/organ in mob_to_check.organs) - found_organ = organ - found_organ.organ_flags ^= ORGAN_FROZEN - - for(var/atom/contained_to_check in object_to_check) //objects held within other objects are added to the processing list, unless that object is something that can hold organs safely - if(!processed_list[contained_to_check] && !istype(contained_to_check, /obj/structure/closet/crate/freezer) && !istype(contained_to_check, /obj/structure/closet/secure_closet/freezer)) - processing_list+= contained_to_check - - index++ - processed_list[object_to_check] = object_to_check - - return - ///Tries to move an atom to an adjacent turf, return TRUE if successful /proc/try_move_adjacent(atom/movable/atom_to_move, trydir) var/turf/atom_turf = get_turf(atom_to_move) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 689efa1b525..0a77222e109 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -204,7 +204,7 @@ Medical HUD! Basic mode needs suit sensors on. if(HAS_TRAIT(src, TRAIT_XENO_HOST)) holder.icon_state = "hudxeno" else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) - if((key || get_ghost(FALSE, FALSE)) && (can_defib() & DEFIB_REVIVABLE_STATES)) // SKYRAT EDIT : OG : if((key || get_ghost(FALSE, TRUE)) && (can_defib() & DEFIB_REVIVABLE_STATES)) + if(can_defib_client()) holder.icon_state = "huddefib" else holder.icon_state = "huddead" diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index f390179ce13..de68f768c34 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -74,6 +74,8 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) var/secure = FALSE var/can_install_electronics = TRUE + var/is_maploaded = FALSE + var/contents_initialized = FALSE /// is this closet locked by an exclusive id, i.e. your own personal locker var/datum/weakref/id_card = null @@ -84,6 +86,14 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /// access types for card reader var/list/access_choices = TRUE + /// Whether this closet is sealed or not. If sealed, it'll have its own internal air + var/sealed = FALSE + + /// Internal gas for this closet. + var/datum/gas_mixture/internal_air + /// Volume of the internal air + var/air_volume = TANK_STANDARD_VOLUME * 3 + /datum/armor/structure_closet melee = 20 bullet = 10 @@ -129,8 +139,9 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) add_to_roundstart_list() // if closed, any item at the crate's loc is put in the contents - if (mapload && !opened) - . = INITIALIZE_HINT_LATELOAD + if (mapload) + is_maploaded = TRUE + . = INITIALIZE_HINT_LATELOAD populate_contents_immediate() var/static/list/loc_connections = list( @@ -148,9 +159,23 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /obj/structure/closet/LateInitialize() . = ..() - if(!opened) + if(!opened && is_maploaded) take_contents() + if(sealed) + var/datum/gas_mixture/external_air = loc.return_air() + if(external_air && is_maploaded) + internal_air = external_air.copy() + else + internal_air = new() + START_PROCESSING(SSobj, src) + +/obj/structure/closet/return_air() + if(sealed) + return internal_air + else + return ..() + //USE THIS TO FILL IT, NOT INITIALIZE OR NEW /obj/structure/closet/proc/PopulateContents() SEND_SIGNAL(src, COMSIG_CLOSET_POPULATE_CONTENTS) @@ -162,10 +187,25 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /obj/structure/closet/Destroy() id_card = null + QDEL_NULL(internal_air) QDEL_NULL(door_obj) GLOB.roundstart_station_closets -= src return ..() +/obj/structure/closet/process(seconds_per_tick) + if(!sealed) + return PROCESS_KILL + process_internal_air(seconds_per_tick) + +/obj/structure/closet/proc/process_internal_air(seconds_per_tick) + if(opened) + var/datum/gas_mixture/current_exposed_air = loc.return_air() + if(!current_exposed_air) + return + if(current_exposed_air.equalize(internal_air)) + var/turf/location = get_turf(src) + location.air_update_turf() + /obj/structure/closet/update_appearance(updates=ALL) . = ..() if(opened || broken || !secure) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index 48424982fb9..1ac7e65884e 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -8,26 +8,26 @@ /// If FALSE, we will protect the first person in the freezer from an explosion / nuclear blast. var/jones = FALSE paint_jobs = null - -/obj/structure/closet/secure_closet/freezer/before_open(mob/living/user, force) - . = ..() - if(!.) - return FALSE - - toggle_organ_decay(src) - return TRUE - -/obj/structure/closet/secure_closet/freezer/after_close(mob/living/user) - . = ..() - toggle_organ_decay(src) - -/obj/structure/closet/secure_closet/freezer/Destroy() - toggle_organ_decay(src) - return ..() - -/obj/structure/closet/secure_closet/freezer/Initialize(mapload) - . = ..() - toggle_organ_decay(src) + sealed = TRUE + + /// The rate at which the internal air mixture cools + var/cooling_rate_per_second = 4 + /// Minimum temperature of the internal air mixture + var/minimum_temperature = T0C - 60 + +/obj/structure/closet/secure_closet/freezer/process_internal_air(seconds_per_tick) + if(opened) + var/datum/gas_mixture/current_exposed_air = loc.return_air() + if(!current_exposed_air) + return + // The internal air won't cool down the external air when the freezer is opened. + internal_air.temperature = max(current_exposed_air.temperature, internal_air.temperature) + return ..() + else + if(internal_air.temperature <= minimum_temperature) + return + var/temperature_decrease_this_tick = min(cooling_rate_per_second * seconds_per_tick, internal_air.temperature - minimum_temperature) + internal_air.temperature -= temperature_decrease_this_tick /obj/structure/closet/secure_closet/freezer/ex_act() if(jones) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 18c09f82783..89b6245a6a0 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -229,22 +229,25 @@ icon_state = "freezer" base_icon_state = "freezer" paint_jobs = null - -/obj/structure/closet/crate/freezer/before_open(mob/living/user, force) - . = ..() - if(!.) - return FALSE - - toggle_organ_decay(src) - return TRUE - -/obj/structure/closet/crate/freezer/after_close(mob/living/user) - . = ..() - toggle_organ_decay(src) - -/obj/structure/closet/crate/freezer/Destroy() - toggle_organ_decay(src) - return ..() + sealed = TRUE + /// The rate at which the internal air mixture cools + var/cooling_rate_per_second = 4 + /// Minimum temperature of the internal air mixture + var/minimum_temperature = T0C - 60 + +/obj/structure/closet/crate/freezer/process_internal_air(seconds_per_tick) + if(opened) + var/datum/gas_mixture/current_exposed_air = loc.return_air() + if(!current_exposed_air) + return + // The internal air won't cool down the external air when the freezer is opened. + internal_air.temperature = max(current_exposed_air.temperature, internal_air.temperature) + return ..() + else + if(internal_air.temperature <= minimum_temperature) + return + var/temperature_decrease_this_tick = min(cooling_rate_per_second * seconds_per_tick, internal_air.temperature - minimum_temperature) + internal_air.temperature -= temperature_decrease_this_tick /obj/structure/closet/crate/freezer/blood name = "blood freezer" diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index c29eb2af58a..1935d6ba417 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -52,7 +52,6 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an connected = new connected(src) connected.connected = src GLOB.bodycontainers += src - toggle_organ_decay(src) register_context() /obj/structure/bodycontainer/add_context(atom/source, list/context, obj/item/held_item, mob/user) @@ -108,7 +107,6 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an /obj/structure/bodycontainer/deconstruct(disassembled = TRUE) if (!(obj_flags & NO_DECONSTRUCTION)) new /obj/item/stack/sheet/iron(loc, 5) - toggle_organ_decay(src) qdel(src) /obj/structure/bodycontainer/container_resist_act(mob/living/user) @@ -135,7 +133,6 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) /obj/structure/bodycontainer/proc/open() - toggle_organ_decay(src) playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) playsound(src, 'sound/effects/roll.ogg', 5, TRUE) var/turf/T = get_step(src, dir) @@ -159,9 +156,13 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an else if(isdead(AM)) continue AM.forceMove(src) - toggle_organ_decay(src) update_appearance() +#define MORGUE_EMPTY 1 +#define MORGUE_NO_MOBS 2 +#define MORGUE_ONLY_BRAINDEAD 3 +#define MORGUE_HAS_REVIVABLE 4 + /* * Morgue */ @@ -177,10 +178,59 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an /// Whether or not this morgue beeps to alert parameds of revivable corpses. var/beeper = TRUE /// The minimum time between beeps. - var/beep_cooldown = 5 SECONDS + var/beep_cooldown = 1 MINUTES + /// Whether this morgue tray has revivables or not + var/morgue_state = MORGUE_EMPTY /// The cooldown to prevent this from spamming beeps. COOLDOWN_DECLARE(next_beep) + /// Internal air of this morgue, for cooling purposes. + var/datum/gas_mixture/internal_air + + /// The rate at which the internal air mixture cools + var/cooling_rate_per_second = 4 + /// Minimum temperature of the internal air mixture + var/minimum_temperature = T0C - 60 + + +/obj/structure/bodycontainer/morgue/Initialize(mapload) + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/structure/bodycontainer/morgue/LateInitialize() + . = ..() + var/datum/gas_mixture/external_air = loc.return_air() + if(external_air) + internal_air = external_air.copy() + else + internal_air = new() + START_PROCESSING(SSobj, src) + +/obj/structure/bodycontainer/morgue/return_air() + return internal_air + +/obj/structure/bodycontainer/morgue/process(seconds_per_tick) + update_morgue_status() + update_appearance(UPDATE_ICON_STATE) + if(morgue_state == MORGUE_HAS_REVIVABLE && beeper && COOLDOWN_FINISHED(src, next_beep)) + playsound(src, 'sound/weapons/gun/general/empty_alarm.ogg', 50, FALSE) //Revive them you blind fucks + COOLDOWN_START(src, next_beep, beep_cooldown) + + if(!connected || connected.loc != src) + var/datum/gas_mixture/current_exposed_air = loc.return_air() + if(!current_exposed_air) + return + // The internal air won't cool down the external air when the freezer is opened. + internal_air.temperature = max(current_exposed_air.temperature, internal_air.temperature) + if(current_exposed_air.equalize(internal_air)) + var/turf/location = get_turf(src) + location.air_update_turf() + else + if(internal_air.temperature <= minimum_temperature) + return + var/temperature_decrease_this_tick = min(cooling_rate_per_second * seconds_per_tick, internal_air.temperature - minimum_temperature) + internal_air.temperature -= temperature_decrease_this_tick + /obj/structure/bodycontainer/morgue/beeper_off name = "secure morgue" desc = "Used to keep bodies in until someone fetches them. Starts with their beeper off." @@ -191,21 +241,62 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an context[SCREENTIP_CONTEXT_ALT_LMB] = "[beeper ? "disable beeper" : "enable beeper"]" return CONTEXTUAL_SCREENTIP_SET -/obj/structure/bodycontainer/morgue/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - . = ..() - if(!istype(arrived, /obj/structure/closet/body_bag)) +/obj/structure/bodycontainer/morgue/proc/update_morgue_status() + if(length(contents) <= 1) + morgue_state = MORGUE_EMPTY + return + + var/list/stored_living = get_all_contents_type(/mob/living) // Search for mobs in all contents. + if(!length(stored_living)) + morgue_state = MORGUE_NO_MOBS + return + + if(obj_flags & EMAGGED) + morgue_state = MORGUE_ONLY_BRAINDEAD return - var/obj/structure/closet/body_bag/arrived_bag = arrived + + for(var/mob/living/occupant as anything in stored_living) + if(occupant.stat == DEAD) + if(iscarbon(occupant)) + var/mob/living/carbon/carbon_occupant = occupant + if(!carbon_occupant.can_defib_client()) + continue + else + if(HAS_TRAIT(occupant, TRAIT_SUICIDED) || HAS_TRAIT(occupant, TRAIT_BADDNA) || (!occupant.key && !occupant.get_ghost(FALSE, TRUE))) + continue + morgue_state = MORGUE_HAS_REVIVABLE + return + morgue_state = MORGUE_ONLY_BRAINDEAD + +/obj/structure/bodycontainer/morgue/proc/handle_bodybag_enter(obj/structure/closet/body_bag/arrived_bag) if(!arrived_bag.tag_name) return name = "[initial(name)] - ([arrived_bag.tag_name])" update_appearance(UPDATE_ICON) +/obj/structure/bodycontainer/morgue/proc/handle_bodybag_exit(obj/structure/closet/body_bag/exited_bag) + name = initial(name) + update_appearance(UPDATE_ICON) + +/obj/structure/bodycontainer/morgue/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + if(istype(arrived, /obj/structure/closet/body_bag)) + return handle_bodybag_enter(arrived) + +/obj/structure/bodycontainer/morgue/close() + . = ..() + update_morgue_status() + update_appearance(UPDATE_ICON_STATE) + /obj/structure/bodycontainer/morgue/Exited(atom/movable/gone, direction) . = ..() if(istype(gone, /obj/structure/closet/body_bag)) - name = initial(name) - update_appearance(UPDATE_ICON) + return handle_bodybag_exit(gone) + +/obj/structure/bodycontainer/morgue/open() + . = ..() + update_morgue_status() + update_appearance(UPDATE_ICON_STATE) /obj/structure/bodycontainer/morgue/examine(mob/user) . = ..() @@ -231,27 +322,20 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an icon_state = "morgue0" return ..() - if(contents.len == 1) // Empty + if(morgue_state == MORGUE_EMPTY) // Empty icon_state = "morgue1" return ..() - var/list/compiled = get_all_contents_type(/mob/living) // Search for mobs in all contents. - if(!length(compiled)) // No mobs? + if(morgue_state == MORGUE_NO_MOBS) // No mobs? icon_state = "morgue3" return ..() - if(!(obj_flags & EMAGGED)) - for(var/mob/living/occupant as anything in compiled) - var/mob/living/mob_occupant = get_mob_or_brainmob(occupant) - if(!mob_occupant.client || HAS_TRAIT(mob_occupant, TRAIT_SUICIDED) || HAS_TRAIT(mob_occupant, TRAIT_BADDNA)) - continue - icon_state = "morgue4" // Revivable - if(mob_occupant.stat == DEAD && beeper && COOLDOWN_FINISHED(src, next_beep)) - playsound(src, 'sound/weapons/gun/general/empty_alarm.ogg', 50, FALSE) //Revive them you blind fucks - COOLDOWN_START(src, next_beep, beep_cooldown) - return ..() + if(morgue_state == MORGUE_HAS_REVIVABLE) + icon_state = "morgue4" // Revivable + return ..() - icon_state = "morgue2" // Dead, brainded mob. + if(morgue_state == MORGUE_ONLY_BRAINDEAD) + icon_state = "morgue2" // Dead, brainded mob. return ..() /obj/structure/bodycontainer/morgue/update_overlays() @@ -261,6 +345,11 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an if(name != initial(name)) . += "[base_icon_state]_label" +#undef MORGUE_EMPTY +#undef MORGUE_NO_MOBS +#undef MORGUE_ONLY_BRAINDEAD +#undef MORGUE_HAS_REVIVABLE + /* * Crematorium */ diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index a95ef30426f..d262dce3e69 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -971,6 +971,9 @@ return DEFIB_POSSIBLE +/mob/living/carbon/proc/can_defib_client() + return (key || get_ghost(FALSE, FALSE)) && (can_defib() & DEFIB_REVIVABLE_STATES) // SKYRAT EDIT : OG : (key || get_ghost(FALSE, TRUE)) && (can_defib() & DEFIB_REVIVABLE_STATES) + /mob/living/carbon/harvest(mob/living/user) if(QDELETED(src)) return diff --git a/code/modules/surgery/organs/internal/_internal_organ.dm b/code/modules/surgery/organs/internal/_internal_organ.dm index eb8629347e6..4e1b6b73701 100644 --- a/code/modules/surgery/organs/internal/_internal_organ.dm +++ b/code/modules/surgery/organs/internal/_internal_organ.dm @@ -43,7 +43,16 @@ /obj/item/organ/internal/on_death(seconds_per_tick, times_fired) //runs decay when outside of a person if(organ_flags & (ORGAN_ROBOTIC | ORGAN_FROZEN)) return - apply_organ_damage(decay_factor * maxHealth * seconds_per_tick) + + if(owner) + if(owner.bodytemperature > T0C) + var/air_temperature_factor = min((owner.bodytemperature - T0C) / T20C, 1) + apply_organ_damage(decay_factor * maxHealth * seconds_per_tick * air_temperature_factor) + else + var/datum/gas_mixture/exposed_air = return_air() + if(exposed_air && exposed_air.temperature > T0C) + var/air_temperature_factor = min((exposed_air.temperature - T0C) / T20C, 1) + apply_organ_damage(decay_factor * maxHealth * seconds_per_tick * air_temperature_factor) /// Called once every life tick on every organ in a carbon's body /// NOTE: THIS IS VERY HOT. Be careful what you put in here