diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 145b9a1874e..61a52124e38 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -595,6 +595,10 @@ #define COMSIG_LIVING_RESTING "living_resting" ///from base of mob/update_transform() #define COMSIG_LIVING_POST_UPDATE_TRANSFORM "living_post_update_transform" +/// Source: /mob/living/proc/apply_status_effect(datum/status_effect/new_instance) +#define COMSIG_LIVING_GAINED_STATUS_EFFECT "living_gained_status_effect" +/// Source: /mob/living/proc/remove_status_effect(datum/status_effect/existing_effect) +#define COMSIG_LIVING_EARLY_LOST_STATUS_EFFECT "living_early_lost_status_effect" // Called before qdel /// From mob/living/try_speak(): (message) #define COMSIG_MOB_TRY_SPEECH "living_vocal_speech" /// Return if the mob cannot speak. @@ -955,7 +959,8 @@ #define COMSIG_HUMAN_REGENERATE_ICONS "human_regenerate_icons" ///From /mob/living/carbon/human/proc/set_species(): (datum/species/old_species) #define COMSIG_HUMAN_SPECIES_CHANGED "human_species_changed" - +/// Source: /mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) +#define COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT "human_early_handle_environment" ///from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration, damage_type) #define COMSIG_HUMAN_CHECK_SHIELDS "human_check_shields" diff --git a/code/__DEFINES/organ_defines.dm b/code/__DEFINES/organ_defines.dm index 34e97a90c74..e18ec9e97ad 100644 --- a/code/__DEFINES/organ_defines.dm +++ b/code/__DEFINES/organ_defines.dm @@ -59,3 +59,6 @@ /// used for species that can see without eyes #define NO_VISION_ORGAN "no_vision_organ" +/// Species organs +#define DRASK_LUNGS_COOLING_START_TEMP 280 +#define DRASK_LUNGS_COOLING_STOP_TEMP 400 diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index b48227307a6..c22533ce055 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -30,6 +30,8 @@ #define STATUS_EFFECT_REGENERATIVE_CORE /datum/status_effect/regenerative_core +#define STATUS_EFFECT_DRASK_COMA /datum/status_effect/drask_coma + #define STATUS_EFFECT_TERROR_REGEN /datum/status_effect/terror/regeneration //over time healing, 125 HP within 25~ seconds #define STATUS_EFFECT_TERROR_FOOD_REGEN /datum/status_effect/terror/food_regen //over time healing for mobs to gain full HP within 25~ seconds diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 9366b9abc46..f3da52eac80 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -782,3 +782,48 @@ /datum/status_effect/drill_payback/on_remove() ..() owner.clear_fullscreen("payback") + +/datum/status_effect/drask_coma + id = "drask_coma" + tick_interval = 2 SECONDS + + var/temp_step + var/cached_sleep_time + +/datum/status_effect/drask_coma/on_creation( + mob/living/new_owner, + duration = 300 SECONDS, + temp_step = 10, + ) + src.duration = duration + src.temp_step = temp_step + + return ..() + +/datum/status_effect/drask_coma/on_apply() + to_chat(owner, span_notice("Ваш метаболизм полностью остановлен.")) + + cached_sleep_time = world.time + owner.AdjustSleeping(duration) + RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(stat_change)) + + return TRUE + +/datum/status_effect/drask_coma/proc/stat_change(datum/source, new_stat, old_stat) + SIGNAL_HANDLER + + if(new_stat == CONSCIOUS || new_stat == DEAD) + qdel(src) + +/datum/status_effect/drask_coma/tick(seconds_between_ticks) + owner.adjust_bodytemperature(-temp_step) + +/datum/status_effect/drask_coma/on_remove() + to_chat(owner, span_notice("Вы чувствуете прилив сил и наконец просыпаетесь.")) + + var/elapsed_time = world.time - cached_sleep_time + + if(elapsed_time < duration) + owner.AdjustSleeping(-(duration - elapsed_time)) + + UnregisterSignal(owner, COMSIG_MOB_STATCHANGE) diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index eac5289d91b..238adcde39a 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -248,6 +248,7 @@ // Create the status effect with our mob + our arguments var/datum/status_effect/new_instance = new new_effect(arguments) if(!QDELETED(new_instance)) + SEND_SIGNAL(src, COMSIG_LIVING_GAINED_STATUS_EFFECT, new_instance) return new_instance @@ -265,6 +266,7 @@ . = FALSE for(var/datum/status_effect/existing_effect as anything in status_effects) if(existing_effect.id == initial(removed_effect.id) && existing_effect.before_remove(arglist(arguments))) + SEND_SIGNAL(src, COMSIG_LIVING_EARLY_LOST_STATUS_EFFECT, existing_effect) qdel(existing_effect) . = TRUE diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 02eadcbee9e..d625200111c 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -291,6 +291,8 @@ if(!environment) return + SEND_SIGNAL(src, COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT, environment) + var/loc_temp = get_temperature(environment) // to_chat(world, "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Thermal protection: [get_main_thermal_protection()] - Fire protection: [thermal_protection + add_fire_protection(loc_temp)] - Heat capacity: [environment_heat_capacity] - Location: [loc] - src: [src]") diff --git a/code/modules/mob/living/carbon/human/species/drask.dm b/code/modules/mob/living/carbon/human/species/drask.dm index 2d2b25dc886..04b47fd6e14 100644 --- a/code/modules/mob/living/carbon/human/species/drask.dm +++ b/code/modules/mob/living/carbon/human/species/drask.dm @@ -1,5 +1,3 @@ -#define DRASK_COOLINGSTARTTEMP 280 -#define ENVIRONMENT_COOLINGSTOPTEMP 400 #define DRASK_PITCH_SHIFT -0.1 // a bit lower emotes /datum/species/drask @@ -95,28 +93,40 @@ var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes) return E.eye_colour -/datum/species/drask/on_species_gain(mob/living/carbon/human/H) +/datum/species/drask/on_species_gain(mob/living/carbon/human/human) . = ..() - add_verb(H, /mob/living/carbon/human/proc/emote_hum) -/datum/species/drask/on_species_loss(mob/living/carbon/human/H) + var/datum/action/innate/drask/coma/coma = locate() in human.actions + + if(!coma) + coma = new + coma.Grant(human) + + add_verb(human, /mob/living/carbon/human/proc/emote_hum) + +/datum/species/drask/on_species_loss(mob/living/carbon/human/human) + . = ..() + + var/datum/action/innate/drask/coma/coma = locate() in human.actions + coma?.Remove(human) + + remove_verb(human, /mob/living/carbon/human/proc/emote_hum) + +/datum/species/drask/handle_life(mob/living/carbon/human/human) . = ..() - remove_verb(H, /mob/living/carbon/human/proc/emote_hum) -/datum/species/drask/handle_life(mob/living/carbon/human/H) - ..() - if(H.stat == DEAD) + if(human.stat == DEAD) return - var/datum/gas_mixture/environment = H.return_air() - if(environment && H.bodytemperature > DRASK_COOLINGSTARTTEMP && environment.temperature <= ENVIRONMENT_COOLINGSTOPTEMP) - H.adjust_bodytemperature(-5) - if(H.bodytemperature < TCRYO) + + if(human.bodytemperature < TCRYO) var/update = NONE - update |= H.heal_overall_damage(2, 4, updating_health = FALSE) - update |= H.heal_damages(tox = 0.5, oxy = 2, clone = 1, updating_health = FALSE) + update |= human.heal_overall_damage(2, 4, updating_health = FALSE) + update |= human.heal_damages(tox = 0.5, oxy = 2, clone = 1, updating_health = FALSE) + if(update) - H.updatehealth() - var/obj/item/organ/external/head/head = H.get_organ(BODY_ZONE_HEAD) + human.updatehealth() + + var/obj/item/organ/external/head/head = human.get_organ(BODY_ZONE_HEAD) head?.undisfigure() /datum/species/drask/handle_reagents(mob/living/carbon/human/H, datum/reagent/R) @@ -127,15 +137,65 @@ if("salglu_solution") if(prob(33)) H.heal_overall_damage(1, 1, updating_health = FALSE) + H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM * H.metabolism_efficiency * H.digestion_ratio) return FALSE + return ..() +/datum/action/innate/drask + +/datum/action/innate/drask/Grant(mob/user) + . = ..() + + if(!. || !isliving(user)) + return FALSE + + return . + +/datum/action/innate/drask/coma + name = "Enter coma" + desc = "Постепенно вводит в состояние комы, понижает температуру тела. Повторная активация способности позволит прервать вход в кому, либо выйти из нее." + + button_icon_state = "heal" + + COOLDOWN_DECLARE(wake_up_cooldown) + +/datum/action/innate/drask/coma/Activate() + var/mob/living/living = owner + + if(!living.has_status_effect(STATUS_EFFECT_DRASK_COMA)) + handle_activation(living) + return + + handle_deactivation(living) + +/datum/action/innate/drask/coma/proc/handle_activation(mob/living/living) + if(living.stat) + return FALSE + + if(!do_after(living, 5 SECONDS, living, ALL, cancel_on_max = TRUE, max_interact_count = 1)) + return FALSE + + living.apply_status_effect(STATUS_EFFECT_DRASK_COMA) + COOLDOWN_START(src, wake_up_cooldown, 10 SECONDS) + + return TRUE + +/datum/action/innate/drask/coma/proc/handle_deactivation(mob/living/living) + if(!COOLDOWN_FINISHED(src, wake_up_cooldown)) + to_chat(living, span_warning("Вы не можете пробудиться сейчас.")) + return FALSE + + if(!do_after(living, 10 SECONDS, living, ALL, cancel_on_max = TRUE, max_interact_count = 1)) + return FALSE + + living.remove_status_effect(STATUS_EFFECT_DRASK_COMA) + + return TRUE + /datum/species/drask/get_emote_pitch(mob/living/carbon/human/H, tolerance) . = ..() . += DRASK_PITCH_SHIFT - -#undef DRASK_COOLINGSTARTTEMP -#undef ENVIRONMENT_COOLINGSTOPTEMP #undef DRASK_PITCH_SHIFT diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index f348dd3cd8a..97e26f68deb 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -334,6 +334,30 @@ cold_level_3_damage = -COLD_GAS_DAMAGE_LEVEL_3 cold_damage_types = list(BRUTE = 0.5, BURN = 0.25) + var/cooling_start_temp = DRASK_LUNGS_COOLING_START_TEMP + var/cooling_stop_temp = DRASK_LUNGS_COOLING_STOP_TEMP + +/obj/item/organ/internal/lungs/drask/insert(mob/living/carbon/target, special = ORGAN_MANIPULATION_DEFAULT) + . = ..() + + if(!.) + return FALSE + + RegisterSignal(owner, COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT, PROC_REF(regulate_temperature)) + +/obj/item/organ/internal/lungs/drask/proc/regulate_temperature(mob/living/source, datum/gas_mixture/environment) + SIGNAL_HANDLER + + if(source.stat == DEAD) + return + + if(owner.bodytemperature > cooling_start_temp && environment.temperature <= cooling_stop_temp) + owner.adjust_bodytemperature(-5) + +/obj/item/organ/internal/lungs/drask/remove(mob/living/user, special = ORGAN_MANIPULATION_DEFAULT) + UnregisterSignal(owner, COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT) + return ..() + /obj/item/organ/internal/lungs/cybernetic name = "cybernetic lungs" desc = "A cybernetic version of the lungs found in traditional humanoid entities. It functions the same as an organic lung and is merely meant as a replacement." diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm index 462f89ebc09..39475908614 100644 --- a/code/modules/surgery/organs/organ.dm +++ b/code/modules/surgery/organs/organ.dm @@ -66,11 +66,15 @@ /obj/item/organ/Destroy() STOP_PROCESSING(SSobj, src) + if(owner) remove(owner, ORGAN_MANIPULATION_NOEFFECT) + QDEL_LIST_ASSOC_VAL(autopsy_data) + if(dna) QDEL_NULL(dna) + return ..() @@ -87,6 +91,7 @@ if(is_robotic() && !species_type) // no DNA for cybernetics, except IPC parts if(update_blood) update_blood() + return if(!dna) @@ -118,6 +123,7 @@ /obj/item/organ/proc/update_blood() if(!dna || (TRAIT_NO_BLOOD in dna.species.inherent_traits)) return + LAZYSET(blood_DNA, dna.unique_enzymes, dna.blood_type) @@ -128,13 +134,17 @@ /obj/item/organ/proc/necrotize(silent = FALSE) if(status & (ORGAN_ROBOT|ORGAN_DEAD)) return FALSE + damage = max_damage status |= ORGAN_DEAD STOP_PROCESSING(SSobj, src) + if(dead_icon && !is_robotic()) icon_state = dead_icon + if(owner && vital) owner.death() + return TRUE @@ -145,6 +155,7 @@ /obj/item/organ/proc/unnecrotize() if(!is_dead()) return FALSE + status &= ~ORGAN_DEAD return TRUE @@ -153,12 +164,15 @@ if(istype(I, /obj/item/stack/nanopaste)) add_fingerprint(user) var/obj/item/stack/nanopaste/nanopaste = I + if(!is_robotic()) to_chat(user, span_warning("The [nanopaste.name] can only be used on robotic bodyparts.")) return ATTACK_CHAIN_PROCEED + if(!nanopaste.use(1)) to_chat(user, span_warning("You need at least one unit of [nanopaste] to proceed.")) return ATTACK_CHAIN_PROCEED + to_chat(user, span_notice("You have repaired the damage on [src].")) rejuvenate() return ATTACK_CHAIN_PROCEED_SUCCESS @@ -184,10 +198,13 @@ // Maybe scale it down a bit, have it REALLY kick in once past the basic infection threshold // Another mercy for surgeons preparing transplant organs germ_level++ + if(germ_level >= INFECTION_LEVEL_ONE) germ_level += rand(2,6) + if(germ_level >= INFECTION_LEVEL_TWO) germ_level += rand(2,6) + if(germ_level >= INFECTION_LEVEL_THREE) necrotize() @@ -211,12 +228,15 @@ for(var/typepath in preserved_holders) if(is_found_within(typepath)) return TRUE + if(istype(loc,/obj/item/mmi)) // So a brain can slowly recover from being left out of an MMI germ_level = max(0, germ_level - 1) return TRUE + if(istype(loc, /mob/living/simple_animal/hostile/headslug) || istype(loc, /obj/item/organ/internal/body_egg/changeling_egg)) germ_level = 0 // weird stuff might happen, best to be safe return TRUE + if(isturf(loc)) var/is_in_freezer = FALSE if(world.time - last_freezer_update_time > freezer_update_period) @@ -342,6 +362,7 @@ /obj/item/organ/proc/heal_internal_damage(amount, robo_repair = FALSE) if(is_robotic() && !robo_repair) return + damage = max(damage - amount, 0) @@ -372,12 +393,13 @@ if(owner?.stat != DEAD && vital && !special) add_attack_logs(user, owner, "Removed vital organ ([src])") owner.death() + owner = null return src /obj/item/organ/proc/replaced(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT) - return // Nothing uses this, it is always overridden + return // A version of `replaced` that "flattens" the process of insertion, making organs "Plug'n'play" @@ -396,6 +418,7 @@ /obj/item/organ/proc/has_damage() if(damage) return TRUE + return FALSE /obj/item/organ/proc/is_robotic() @@ -404,6 +427,7 @@ /obj/item/organ/serialize() var/data = ..() + if(status != 0) data["status"] = status @@ -411,6 +435,7 @@ // the owner if(!(owner && dna.unique_enzymes == owner.dna.unique_enzymes)) data["dna"] = dna.serialize() + return data diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm index 5d1962879be..08749b1b317 100644 --- a/code/modules/surgery/organs/organ_external.dm +++ b/code/modules/surgery/organs/organ_external.dm @@ -182,8 +182,10 @@ return var/obj/item/organ/external/replaced = owner.bodyparts_by_name[limb_zone] + if(!isnull(replaced)) replaced.remove(target, ORGAN_MANIPULATION_NOEFFECT) + owner.bodyparts_by_name[limb_zone] = src owner.bodyparts |= src diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index e929d3fe9e6..08ca28a9282 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -18,6 +18,7 @@ if(iscarbon(loc)) insert(loc) + if(species_type == /datum/species/diona) AddComponent(/datum/component/diona_internals) @@ -31,6 +32,7 @@ do_pickup_animation(src, target) var/obj/item/organ/internal/replaced = target.get_organ_slot(slot) + if(replaced) replaced.remove(target, ORGAN_MANIPULATION_NOEFFECT) @@ -45,6 +47,7 @@ stack_trace("[src] attempted to insert into a [parent_organ_zone], but [parent_organ_zone] wasn't an organ! [atom_loc_line(h_target)]") else LAZYOR(parent.internal_organs, src) + h_target.update_int_organs() loc = null @@ -76,10 +79,13 @@ if(iscarbon(organ_owner)) organ_owner.internal_organs -= src + if(organ_owner.internal_organs_slot[slot] == src) organ_owner.internal_organs_slot[slot] = null + if(!special) send_signal = TRUE + if(vital && !special && organ_owner.stat != DEAD) organ_owner.death() @@ -107,6 +113,7 @@ /obj/item/organ/internal/emp_act(severity) if(!is_robotic() || emp_proof) return + switch(severity) if(1) internal_receive_damage(20, silent = TRUE) @@ -138,6 +145,7 @@ /obj/item/organ/internal/proc/prepare_eat() if(is_robotic()) return //no eating cybernetic implants! + var/obj/item/reagent_containers/food/snacks/organ/S = new S.name = name S.desc = desc @@ -151,6 +159,7 @@ /obj/item/organ/internal/attempt_become_organ(obj/item/organ/external/parent, mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT) if(parent_organ_zone != parent.limb_zone) return FALSE + insert(target, special) return TRUE @@ -172,6 +181,7 @@ return ..() var/obj/item/reagent_containers/food/snacks/snack = prepare_eat() + if(!snack) return ATTACK_CHAIN_PROCEED @@ -198,9 +208,11 @@ H.icon_base = "[slot]-c" H.dead_icon = "[slot]-c-off" H.update_icon() + else if("[slot]-c" in states) //Give the robotic organ its robotic organ icons if they exist. icon = icon('icons/obj/surgery.dmi') icon_state = "[slot]-c" + name = "cybernetic [slot]" ..() //Go apply all the organ flags/robotic statuses. @@ -217,12 +229,14 @@ for(var/datum/disease/appendicitis/A in M.diseases) A.cure() inflamed = TRUE + update_icon() . = ..() /obj/item/organ/internal/appendix/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) ..() + if(inflamed) var/datum/disease/appendicitis/D = new D.Contract(M) @@ -230,8 +244,10 @@ /obj/item/organ/internal/appendix/prepare_eat() var/obj/S = ..() + if(inflamed) S.reagents.add_reagent("????", 5) + return S @@ -263,8 +279,10 @@ var/light_count = T.get_lumcount()*10 if(light_count > 4 && obj_integrity > 0) //Die in the light obj_integrity-- + else if(light_count < 2 && obj_integrity < max_integrity) //Heal in the dark obj_integrity++ + if(obj_integrity <= 0) visible_message(span_warning("[src] collapses in on itself!")) qdel(src) @@ -287,6 +305,7 @@ /obj/item/organ/internal/honktumor/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) ..() + M.force_gene_block(GLOB.clumsyblock, TRUE) M.force_gene_block(GLOB.comicblock, TRUE) organhonked = world.time @@ -334,6 +353,7 @@ /obj/item/organ/internal/honktumor/cursed/on_life() //No matter what you do, no matter who you are, no matter where you go, you're always going to be a fat, stuttering dimwit. ..() + owner.setBrainLoss(80) owner.set_nutrition(9000) owner.overeatduration = 9000 @@ -379,13 +399,16 @@ if(ishuman(owner)) var/mob/living/carbon/human/H = owner var/obj/item/organ/external/head/head_organ = H.get_organ(BODY_ZONE_HEAD) + if(!(head_organ.h_style == "Very Long Hair" || head_organ.h_style == "Mohawk")) if(prob(10)) head_organ.h_style = "Mohawk" else head_organ.h_style = "Very Long Hair" + head_organ.hair_colour = "#D8C078" H.update_hair() + if(!(head_organ.f_style == "Very Long Beard")) head_organ.f_style = "Very Long Beard" head_organ.facial_colour = "#D8C078" @@ -396,7 +419,9 @@ ..() if(!ishuman(owner)) return + var/germs_mod = owner.dna.species.germs_growth_mod * owner.physiology.germs_growth_mod + if(germ_level >= INFECTION_LEVEL_TWO && prob(3 * germs_mod)) // big message from every 1 damage is not good. If germs growth rate is big, it will spam the chat. internal_receive_damage(1, silent = prob(30 * germs_mod)) @@ -404,16 +429,19 @@ /mob/living/carbon/human/proc/check_infections() var/list/infections = list() + for(var/obj/item/organ/internal/organ as anything in internal_organs) if(organ.germ_level > 0) infections.Add(organ) + return infections /mob/living/carbon/human/proc/check_damaged_organs() var/list/damaged = list() + for(var/obj/item/organ/internal/organ as anything in internal_organs) if(organ.damage > 0) damaged.Add(organ) - return damaged + return damaged