diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index 1eafe07b13..2f82e49462 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -68,6 +68,20 @@
#define TEMPERATURE_DAMAGE_COEFFICIENT 1.5 //This is used in handle_temperature_damage() for humans, and in reagents that affect body temperature. Temperature damage is multiplied by this amount.
+#define SYNTH_PASSIVE_HEAT_GAIN 10 //Degrees C per handle_environment() Synths passively heat up. Mitigated by cooling efficiency. Can lead to overheating if not managed.
+#define SYNTH_MAX_PASSIVE_GAIN_TEMP 250 //Degrees C that a synth can be heated up to by their internal heat gain, provided their cooling is insufficient to mitigate it.
+#define SYNTH_MIN_PASSIVE_COOLING_TEMP -30 //Degrees C a synth can cool towards at very high cooling efficiency.
+#define SYNTH_HEAT_EFFICIENCY_COEFF 0.005 //How quick the difference between the Synth and the environment starts to matter. The smaller the higher the difference has to be for the same change.
+#define SYNTH_SINGLE_INFLUENCE_COOLING_EFFECT_CAP 3 //How big can the multiplier for heat / pressure cooling be in an optimal environment
+#define SYNTH_TOTAL_ENVIRONMENT_EFFECT_CAP 2 //How big of an multiplier can the environment give in an optimal scenario (maximum efficiency in the end is at a lower cap, this mostly counters low coolant levels)
+#define SYNTH_MAX_COOLING_EFFICIENCY 1.5 //The maximum possible cooling efficiency one can achieve at optimal conditions.
+#define SYNTH_ACTIVE_COOLING_TEMP_BOUNDARY 10 //The minimum distance from room temperature a Synth needs to have for active cooling to actively cool.
+#define SYNTH_ACTIVE_COOLING_LOW_PRESSURE_THRESHOLD 0.05 //At how much percentage of default pressure (or lower) active cooling gets a massive cost penalty.
+#define SYNTH_ACTIVE_COOLING_LOW_PRESSURE_PENALTY 2.5 //By how much is active cooling cost multiplied if in a very-low-pressure environment?
+#define SYNTH_ACTIVE_COOLING_MIN_ADJUSTMENT 5 //What is the minimum amount of temp you move towards the target point, even if it would be less with default calculations?
+#define SYNTH_INTEGRATION_COOLANT_PENALTY 0.4 //Integrating coolant is multiplied with this for calculation of impact on passive cooling.
+#define SYNTH_INTEGRATION_COOLANT_CAP 0.25 //Integrating coolant is capped at counting as current_blood * this number. This is so you can't just run on salglu or whatever.
+
#define BODYTEMP_NORMAL 310.15 //The natural temperature for a body
#define BODYTEMP_AUTORECOVERY_DIVISOR 11 //This is the divisor which handles how much of the temperature difference between the current body temperature and 310.15K (optimal temperature) humans auto-regenerate each tick. The higher the number, the slower the recovery. This is applied each tick, so long as the mob is alive.
#define BODYTEMP_AUTORECOVERY_MINIMUM 12 //Minimum amount of kelvin moved toward 310K per tick. So long as abs(310.15 - bodytemp) is more than 50.
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 9538cf91fa..0609b9dabe 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -79,7 +79,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isangel(A) (is_species(A, /datum/species/angel))
#define ismush(A) (is_species(A, /datum/species/mush))
#define isshadow(A) (is_species(A, /datum/species/shadow))
-#define isrobotic(A) (is_species(A, /datum/species/ipc) || is_species(A, /datum/species/synthliz))
+#define isrobotic(A) (is_species(A, /datum/species/ipc) || is_species(A, /datum/species/synthliz) || is_species(A, /datum/species/mammal/synthetic))
#define isdwarf(A) (is_species(A, /datum/species/dwarf))
// Citadel specific species
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index ca326ece5f..edc6f58eaf 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -121,6 +121,7 @@
#define TRAIT_ROBOTIC_ORGANISM "robotic_organism"
#define TRAIT_ROBOT_RADSHIELDING "robot_radshielding"
#define TRAIT_NOBREATH "no_breath"
+#define TRAIT_AUXILIARY_LUNGS "auxiliary_lungs" //Lungs not neccessary required due to nobreath, but provides some other helpful function.
#define TRAIT_ANTIMAGIC "anti_magic"
#define TRAIT_HOLY "holy"
#define TRAIT_DEPRESSION "depression"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index bbe801cfc2..a3a1a35888 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -55,6 +55,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_ROBOTIC_ORGANISM" = TRAIT_ROBOTIC_ORGANISM,
"TRAIT_ROBOT_RADSHIELDING" = TRAIT_ROBOT_RADSHIELDING,
"TRAIT_NOBREATH" = TRAIT_NOBREATH,
+ "TRAIT_AUXILIARY_LUNGS" = TRAIT_AUXILIARY_LUNGS,
"TRAIT_ANTIMAGIC" = TRAIT_ANTIMAGIC,
"TRAIT_HOLY" = TRAIT_HOLY,
"TRAIT_DEPRESSION" = TRAIT_DEPRESSION,
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 36a3cd1f1a..144a38e728 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -35,6 +35,8 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/devil/soul_counter/devilsouldisplay
+ var/atom/movable/screen/synth/coolant_counter/coolant_display
+
var/atom/movable/screen/action_intent
var/atom/movable/screen/zone_select
var/atom/movable/screen/pull_icon
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index e4a9dc24e8..83b8d7be48 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -80,6 +80,98 @@
icon_state = "power_display"
screen_loc = ui_lingchemdisplay
+#define ui_coolant_display "EAST,SOUTH+3:15"
+
+/atom/movable/screen/synth
+ invisibility = INVISIBILITY_ABSTRACT
+
+
+/atom/movable/screen/synth/proc/clear()
+ invisibility = INVISIBILITY_ABSTRACT
+
+/atom/movable/screen/synth/proc/update_counter(mob/living/carbon/human/owner)
+ invisibility = 0
+
+/atom/movable/screen/synth/coolant_counter
+ icon = 'icons/mob/screen_synth.dmi'
+ name = "Coolant System Readout"
+ icon_state = "coolant-3-1"
+ screen_loc = ui_coolant_display
+ var/jammed = 0
+
+/atom/movable/screen/synth/coolant_counter/update_counter(mob/living/carbon/owner)
+ ..()
+ var/valuecolor = "#ff2525"
+ if(owner.stat == DEAD)
+ maptext = "
ERR-0F
"
+ icon_state = "coolant-3-1"
+ return
+ var/coolant_efficiency
+ var/coolant
+ if(!jammed)
+ coolant_efficiency = owner.get_cooling_efficiency()
+ coolant = owner.blood_volume
+ else
+ coolant_efficiency = rand(1, 15) / 10
+ coolant = rand(1, 600)
+ jammed--
+ if(coolant > BLOOD_VOLUME_SAFE * owner.blood_ratio) //I unfortunately have to use this else-if stack because switch doesn't support variables.
+ valuecolor = "#4bbd34"
+ else if(coolant > BLOOD_VOLUME_OKAY * owner.blood_ratio)
+ valuecolor = "#dabb0d"
+ else if(coolant > BLOOD_VOLUME_BAD * owner.blood_ratio)
+ valuecolor = "#dd8109"
+ else if(coolant > BLOOD_VOLUME_SURVIVE * owner.blood_ratio)
+ valuecolor = "#e7520d"
+ maptext = "[round((coolant / (BLOOD_VOLUME_NORMAL * owner.blood_ratio)) * 100, 1)]
"
+
+ var/efficiency_suffix
+ var/state_suffix
+ switch(coolant_efficiency)
+ if(-INFINITY to 0.4)
+ efficiency_suffix = "1"
+ if(0.4 to 0.75)
+ efficiency_suffix = "2"
+ if(0.75 to 0.95)
+ efficiency_suffix = "3"
+ if(0.95 to 1.3)
+ efficiency_suffix = "4"
+ else
+ efficiency_suffix = "5"
+ var/obj/item/organ/lungs/ipc/L = owner.getorganslot(ORGAN_SLOT_LUNGS)
+ if(istype(L) && L.is_cooling)
+ state_suffix = "2"
+ else
+ state_suffix = "1"
+ icon_state = "coolant-[efficiency_suffix]-[state_suffix]"
+
+/atom/movable/screen/synth/coolant_counter/examine(mob/user)
+ . = ..()
+ var/mob/living/carbon/human/owner = hud.mymob
+ if(owner.stat == DEAD)
+ return
+ var/coolant
+ var/total_efficiency
+ var/environ_efficiency
+ var/suitlink_efficiency
+ if(!jammed)
+ coolant = owner.blood_volume
+ total_efficiency = owner.get_cooling_efficiency()
+ environ_efficiency = owner.get_environment_cooling_efficiency()
+ suitlink_efficiency = owner.check_suitlinking()
+ else
+ coolant = rand(1, 600)
+ total_efficiency = rand(1, 15) / 10
+ environ_efficiency = rand(1, 20) / 10
+ . += "Performing internal cooling system diagnostics:"
+ . += "Coolant level: [coolant] units, [round((coolant / (BLOOD_VOLUME_NORMAL * owner.blood_ratio)) * 100, 0.1)] percent"
+ . += "Current Cooling Efficiency: [round(total_efficiency * 100, 0.1)] percent, [suitlink_efficiency ? "active suitlink detected, guaranteeing [suitlink_efficiency * 100]% environmental cooling efficiency." : "environment viability: [round(environ_efficiency * 100, 0.1)] percent."]"
+
+/atom/movable/screen/synth/coolant_counter/proc/jam(amount, cap = 20)
+ if(jammed > cap) //Preserve previous more impactful event.
+ return
+ jammed = min(jammed + amount, cap)
+
/datum/hud/human/New(mob/living/carbon/human/owner)
..()
owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness)
@@ -359,6 +451,10 @@
sunlight_display.hud = src
infodisplay += sunlight_display
+ coolant_display = new /atom/movable/screen/synth/coolant_counter //Coolant & cooling efficiency readouts for Synths.
+ coolant_display.hud = src
+ infodisplay += coolant_display
+
zone_select = new /atom/movable/screen/zone_sel()
zone_select.icon = ui_style
zone_select.hud = src
diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm
index 74cd53b0ae..b3c8a6422f 100644
--- a/code/controllers/subsystem/processing/quirks.dm
+++ b/code/controllers/subsystem/processing/quirks.dm
@@ -48,6 +48,13 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
cli.prefs.save_character()
if (!silent && LAZYLEN(cut))
to_chat(to_chat_target || user, "Some quirks have been cut from your character because of these quirks conflicting with your job assignment: [english_list(cut)].")
+
+ var/mob/living/carbon/human/H = user
+ if(istype(H) && H.dna?.species)
+ var/datum/species/S = H.dna.species
+ if(S.remove_blacklisted_quirks(H))
+ to_chat(to_chat_target || user, "Some quirks have been cut from your character due to them conflicting with your species: [english_list(S.removed_quirks)]")
+
/datum/controller/subsystem/processing/quirks/proc/quirk_path_by_name(name)
return quirks[name]
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 3ded0310dd..a8e76d04ca 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -325,7 +325,7 @@ GENETICS SCANNER
var/breathes = TRUE
var/blooded = TRUE
if(C.dna && C.dna.species)
- if(HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
+ if(!HAS_TRAIT_FROM(C, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) && HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
breathes = FALSE
if(NOBLOOD in C.dna.species.species_traits)
blooded = FALSE
@@ -434,12 +434,13 @@ GENETICS SCANNER
if(R)
blood_type = R.name
+
if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_SAFE * C.blood_ratio) && (C.scan_blood_volume() + C.integrating_blood) > (BLOOD_VOLUME_OKAY*C.blood_ratio))
- msg += "LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]\n"
+ msg += "LOW [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]\n"
else if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_OKAY * C.blood_ratio))
- msg += "CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]\n"
+ msg += "CRITICAL [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]\n"
else
- msg += "Blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]\n"
+ msg += "[HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "Coolant" : "Blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]\n"
var/cyberimp_detect
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index f888927411..0bd3083a59 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -488,6 +488,9 @@
icon_state = "nanogel"
var/being_applied = FALSE //No doafter stacking.
+/obj/item/stack/medical/nanogel/one
+ amount = 1
+
/obj/item/stack/medical/nanogel/try_heal(mob/living/M, mob/user, silent = FALSE)
if(being_applied)
to_chat(user, "You are already applying [src]!")
diff --git a/code/modules/antagonists/abductor/equipment/glands/heal.dm b/code/modules/antagonists/abductor/equipment/glands/heal.dm
index 8917e1661e..e995826af6 100644
--- a/code/modules/antagonists/abductor/equipment/glands/heal.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/heal.dm
@@ -23,7 +23,7 @@
return
var/obj/item/organ/lungs/lungs = owner.getorganslot(ORGAN_SLOT_LUNGS)
- if((!lungs && !HAS_TRAIT(owner, TRAIT_NOBREATH)) || (lungs && (istype(lungs, /obj/item/organ/lungs/cybernetic))))
+ if((!lungs && (HAS_TRAIT_FROM(owner, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) || !HAS_TRAIT(owner, TRAIT_NOBREATH))) || (lungs && (istype(lungs, /obj/item/organ/lungs/cybernetic))))
replace_lungs(lungs)
return
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 2a05639ad9..eabf2e2d0e 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -65,29 +65,30 @@
blood_volume = min(BLOOD_VOLUME_NORMAL, blood_volume + 0.5 * nutrition_ratio)
//Effects of bloodloss
- var/word = pick("dizzy","woozy","faint")
- var/blood_effect_volume = blood_volume + integrating_blood
- switch(blood_effect_volume)
- if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS)
- if(prob(10))
- to_chat(src, "You feel terribly bloated.")
- if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
- if(prob(5))
- to_chat(src, "You feel [word].")
- adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1))
- if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
- adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
- if(prob(5))
- blur_eyes(6)
- to_chat(src, "You feel very [word].")
- if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD)
- adjustOxyLoss(5)
- if(prob(15))
- Unconscious(rand(20,60))
- to_chat(src, "You feel extremely [word].")
- if(-INFINITY to BLOOD_VOLUME_SURVIVE)
- if(!HAS_TRAIT(src, TRAIT_NODEATH))
- death()
+ if(!HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM)) //Synths are immune to direct consequences of bloodloss, instead suffering penalties to heat exchange.
+ var/word = pick("dizzy","woozy","faint")
+ var/blood_effect_volume = blood_volume + integrating_blood
+ switch(blood_effect_volume)
+ if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS)
+ if(prob(10))
+ to_chat(src, "You feel terribly bloated.")
+ if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
+ if(prob(5))
+ to_chat(src, "You feel [word].")
+ adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1))
+ if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
+ adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
+ if(prob(5))
+ blur_eyes(6)
+ to_chat(src, "You feel very [word].")
+ if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD)
+ adjustOxyLoss(5)
+ if(prob(15))
+ Unconscious(rand(20,60))
+ to_chat(src, "You feel extremely [word].")
+ if(-INFINITY to BLOOD_VOLUME_SURVIVE)
+ if(!HAS_TRAIT(src, TRAIT_NODEATH))
+ death()
var/temp_bleed = 0
//Bleeding out
diff --git a/code/modules/mob/living/carbon/handle_corruption.dm b/code/modules/mob/living/carbon/handle_corruption.dm
index 5ac2a2f616..b56752453b 100644
--- a/code/modules/mob/living/carbon/handle_corruption.dm
+++ b/code/modules/mob/living/carbon/handle_corruption.dm
@@ -37,7 +37,7 @@
var/list/whatmighthappen = list()
whatmighthappen += list("avoided" = 3, "dropthing" = 1, "movetile" = 1, "shortdeaf" = 1, "flopover" = 1, "nutriloss" = 1, "selfflash" = 1, "harmies" = 1)
if(corruption >= CORRUPTION_THRESHHOLD_MAJOR)
- whatmighthappen += list("longdeaf" = 1, "longknockdown" = 1, "shortlimbdisable" = 1, "shortblind" = 1, "shortstun" = 1, "shortmute" = 1, "vomit" = 1, "halluscinate" = 1)
+ whatmighthappen += list("longdeaf" = 1, "longknockdown" = 1, "shortlimbdisable" = 1, "shortblind" = 1, "shortstun" = 1, "shortmute" = 1, "vomit" = 1, "hallucinate" = 1, "jamcoolanthud" = 1)
if(corruption >= CORRUPTION_THRESHHOLD_CRITICAL)
whatmighthappen += list("receporgandamage" = 1, "longlimbdisable" = 1, "blindmutedeaf" = 1, "longstun" = 1, "sleep" = 1, "inducetrauma" = 1, "amplifycorrupt" = 1, "changetemp" = 1)
var/event = pickweight(whatmighthappen)
@@ -97,8 +97,10 @@
if("vomit")
to_chat(src, "Ejecting contaminant.")
vomit()
- if("halluscinate")
+ if("hallucinate")
hallucination += 20 //Doesn't give a cue
+ if("jamcoolanthud")
+ hud_used.coolant_display.jam(10)
if("receporgandamage")
adjustOrganLoss(ORGAN_SLOT_EARS, rand(10, 20))
adjustOrganLoss(ORGAN_SLOT_EYES, rand(10, 20))
@@ -159,7 +161,7 @@
/mob/living/carbon/proc/forcesleep(time = 100)
to_chat(src, "Preparations complete, powering down.")
- Sleeping(time, 0)
+ Sleeping(time)
#undef CORRUPTION_CHECK_INTERVAL
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 0e75595c75..c3fb82de36 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -249,16 +249,17 @@
if(DISGUST_LEVEL_DISGUSTED to INFINITY)
msg += "[t_He] look[p_s()] extremely disgusted.\n"
- var/apparent_blood_volume = blood_volume
- if(dna.species.use_skintones && skin_tone == "albino")
- apparent_blood_volume -= 150 // enough to knock you down one tier
- switch(apparent_blood_volume)
- if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
- msg += "[t_He] [t_has] pale skin.\n"
- if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
- msg += "[t_He] look[p_s()] like pale death.\n"
- if(-INFINITY to BLOOD_VOLUME_BAD)
- msg += "[t_He] resemble[p_s()] a crushed, empty juice pouch.\n"
+ if(!HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
+ var/apparent_blood_volume = blood_volume
+ if(dna.species.use_skintones && skin_tone == "albino")
+ apparent_blood_volume -= 150 // enough to knock you down one tier
+ switch(apparent_blood_volume)
+ if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
+ msg += "[t_He] [t_has] pale skin.\n"
+ if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
+ msg += "[t_He] look[p_s()] like pale death.\n"
+ if(-INFINITY to BLOOD_VOLUME_BAD)
+ msg += "[t_He] resemble[p_s()] a crushed, empty juice pouch.\n"
if(bleedsuppress)
msg += "[t_He] [t_is] embued with a power that defies bleeding.\n" // only statues and highlander sword can cause this so whatever
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 16ab351d64..71573abc90 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -416,6 +416,7 @@
severity *= 0.5
var/do_not_stun = FALSE
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
+ hud_used?.coolant_display.jam(round(severity / 10, 1)) //Messes up the cooling system readout.
severity *= 0.5 //Robotpeople take less limb damage, but instead suffer system corruption (see carbon emp_act)
do_not_stun = TRUE
for(var/obj/item/bodypart/L in src.bodyparts)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 85f01e63b3..1ea28211af 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -30,6 +30,8 @@
/mob/living/carbon/human/PhysicalLife(seconds, times_fired)
if(!(. = ..()))
return
+ if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM) && hud_used)
+ hud_used.coolant_display.update_counter(src)
//Update our name based on whether our face is obscured/disfigured
name = get_visible_name()
@@ -76,9 +78,23 @@
/mob/living/carbon/human/check_breath(datum/gas_mixture/breath)
- var/L = getorganslot(ORGAN_SLOT_LUNGS)
+ if(breath && HAS_TRAIT(src, TRAIT_NOBREATH) && HAS_TRAIT(src, TRAIT_AUXILIARY_LUNGS)) //Something something bz and synth cooling systems interacting (in reality, this only exists to not make robot lings too strong)
+ var/total_moles = breath.total_moles()
+ var/pressure = breath.return_pressure()
+ #define PP_MOLES(X) ((X / total_moles) * pressure)
+ #define PP(air, gas) PP_MOLES(air.get_moles(gas))
+ var/bz_pp = PP(breath, GAS_BZ)
+ if(bz_pp > 1)
+ reagents.add_reagent(/datum/reagent/bz_metabolites,5)
+ else if(bz_pp > 0.1)
+ reagents.add_reagent(/datum/reagent/bz_metabolites,1)
+ #undef PP_MOLES
+ #undef PP
+ var/L = getorganslot(ORGAN_SLOT_LUNGS)
if(!L)
+ if(HAS_TRAIT(src, TRAIT_NOBREATH))
+ return
if(health >= crit_threshold)
adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1)
else if(!HAS_TRAIT(src, TRAIT_NOCRITDAMAGE))
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index d81297d41f..1c5bdb6114 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -108,6 +108,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/burnmod = 1
///multiplier for damage from cold temperature
var/coldmod = 1
+ ///moves their safe minimum temp by this value.
+ var/cold_offset = 0
+ ///moves their safe maximum temp by this value.
+ var/hot_offset = 0
///multiplier for damage from hot temperature
var/heatmod = 1
///multiplier for stun durations
@@ -147,6 +151,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/list/blacklisted_quirks = list() // Quirks that will be removed upon gaining this species, to be defined by species
var/list/removed_quirks = list() // Quirks that got removed due to being blacklisted, and will be restored when on_species_loss() is called
+ var/balance_point_values = FALSE //If true, will balance point values on species gain after removing blacklisted quirks. Use this for roundstart species with blacklisted quirks that people may attempt to use to powergame trait points.
///Punch-specific attack verb.
var/attack_verb = "punch"
@@ -331,7 +336,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/should_have_brain = TRUE
var/should_have_heart = !(NOBLOOD in species_traits)
- var/should_have_lungs = !(TRAIT_NOBREATH in inherent_traits)
+ var/should_have_lungs = ((TRAIT_AUXILIARY_LUNGS in inherent_traits) || !(TRAIT_NOBREATH in inherent_traits))
var/should_have_appendix = !(TRAIT_NOHUNGER in inherent_traits)
var/should_have_eyes = TRUE
var/should_have_ears = TRUE
@@ -569,17 +574,39 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
for(var/obj/item/bodypart/B in C.bodyparts)
B.change_bodypart_status(initial(B.status), FALSE, TRUE)
+ if((TRAIT_ROBOTIC_ORGANISM in inherent_traits) && C.hud_used)
+ C.hud_used.coolant_display.clear()
+
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
// shamelessly inspired by antag_datum.remove_blacklisted_quirks()
/datum/species/proc/remove_blacklisted_quirks(mob/living/carbon/C)
- var/mob/living/L = C.mind?.current
- if(istype(L))
- for(var/q in L.roundstart_quirks)
- var/datum/quirk/Q = q
- if(Q.type in blacklisted_quirks)
- qdel(Q)
- removed_quirks += Q.type
+ . = 0
+ if(istype(C))
+ if(!balance_point_values)
+ for(var/q in C.roundstart_quirks)
+ var/datum/quirk/Q = q
+ if(Q.type in blacklisted_quirks)
+ removed_quirks += Q.type
+ . += 1
+ qdel(Q)
+ else
+ var/point_overhead = 0
+ for(var/datum/quirk/Q as anything in C.roundstart_quirks)
+ if(Q.type in blacklisted_quirks)
+ point_overhead -= Q.value
+ removed_quirks += Q.type
+ . += 1
+ qdel(Q)
+ if(point_overhead)
+ for(var/datum/quirk/Q as anything in C.roundstart_quirks)
+ if(Q.value > 0)
+ point_overhead -= Q.value
+ removed_quirks += Q.type
+ . += 1
+ qdel(Q)
+ if(!point_overhead)
+ break
// restore any quirks that we removed
/datum/species/proc/restore_quirks(mob/living/carbon/C)
@@ -1165,7 +1192,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/takes_crit_damage = !HAS_TRAIT(H, TRAIT_NOCRITDAMAGE)
if((H.health < H.crit_threshold) && takes_crit_damage)
- H.adjustBruteLoss(1)
+ if(!HAS_TRAIT(H, TRAIT_ROBOTIC_ORGANISM))
+ H.adjustBruteLoss(1)
+ else
+ H.adjustFireLoss(1) //Robots melt instead of taking brute.
/datum/species/proc/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
@@ -1874,7 +1904,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(BODY_ZONE_HEAD)
if(!I.get_sharpness() && armor_block < 50)
if(prob(I.force))
- if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
+ if(HAS_TRAIT(H, TRAIT_ROBOTIC_ORGANISM))
H.adjustToxLoss(5, toxins_type = TOX_SYSCORRUPT) //Bonk! - Effectively 5 bonus damage
else
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20)
@@ -2171,15 +2201,20 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
//Thermal protection (insulation) has mixed benefits in two situations (hot in hot places, cold in hot places)
if(!H.on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases
var/natural = 0
+ var/cooling_efficiency = 1
if(H.stat != DEAD)
natural = H.natural_bodytemperature_stabilization()
+ cooling_efficiency = H.get_cooling_efficiency()
+
+ if(HAS_TRAIT(H, TRAIT_ROBOTIC_ORGANISM)) //Synths by default slowly heat up and need to lose said heat to the environment or active cooling. If you have very high cooling efficiency, you instead passively cool.
+ H.adjust_bodytemperature(SYNTH_PASSIVE_HEAT_GAIN * (1 - cooling_efficiency), (T0C + SYNTH_MIN_PASSIVE_COOLING_TEMP), (T0C + SYNTH_MAX_PASSIVE_GAIN_TEMP))
var/thermal_protection = 1
if(loc_temp < H.bodytemperature) //Place is colder than we are
thermal_protection -= H.get_thermal_protection(loc_temp, TRUE) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(H.bodytemperature < BODYTEMP_NORMAL) //we're cold, insulation helps us retain body heat and will reduce the heat we lose to the environment
- H.adjust_bodytemperature((thermal_protection+1)*natural + max(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_COLD_DIVISOR, BODYTEMP_COOLING_MAX))
+ H.adjust_bodytemperature((thermal_protection+1)*natural + max((thermal_protection * (loc_temp - H.bodytemperature) * cooling_efficiency) / BODYTEMP_COLD_DIVISOR, BODYTEMP_COOLING_MAX))
else //we're sweating, insulation hinders our ability to reduce heat - and it will reduce the amount of cooling you get from the environment
- H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + max((thermal_protection * (loc_temp - H.bodytemperature) + BODYTEMP_NORMAL - H.bodytemperature) / BODYTEMP_COLD_DIVISOR , BODYTEMP_COOLING_MAX)) //Extra calculation for hardsuits to bleed off heat
+ H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + max(((thermal_protection * (loc_temp - H.bodytemperature) + BODYTEMP_NORMAL - H.bodytemperature) * cooling_efficiency) / BODYTEMP_COLD_DIVISOR , BODYTEMP_COOLING_MAX)) //Extra calculation for hardsuits to bleed off heat
else //Place is hotter than we are
thermal_protection -= H.get_thermal_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(H.bodytemperature < BODYTEMP_NORMAL) //and we're cold, insulation enhances our ability to retain body heat but reduces the heat we get from the environment
@@ -2203,7 +2238,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.throw_alert("tempfeel", /atom/movable/screen/alert/hot, 3)
// +/- 50 degrees from 310K is the 'safe' zone, where no damage is dealt.
- if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !HAS_TRAIT(H, TRAIT_RESISTHEAT))
+ if(H.bodytemperature > (BODYTEMP_HEAT_DAMAGE_LIMIT + hot_offset) && !HAS_TRAIT(H, TRAIT_RESISTHEAT))
//Body temperature is too hot.
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
@@ -2231,11 +2266,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.emote("scream")
H.apply_damage(burn_damage, BURN)
- else if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !HAS_TRAIT(H, TRAIT_RESISTCOLD))
+ else if(H.bodytemperature < (BODYTEMP_COLD_DAMAGE_LIMIT + cold_offset) && !HAS_TRAIT(H, TRAIT_RESISTCOLD))
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "cold", /datum/mood_event/cold)
//Apply cold slowdown
- H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = ((BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
+ H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = (((BODYTEMP_COLD_DAMAGE_LIMIT + cold_offset) - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
switch(H.bodytemperature)
if(200 to BODYTEMP_COLD_DAMAGE_LIMIT)
H.throw_alert("temp", /atom/movable/screen/alert/shiver, 1)
diff --git a/code/modules/mob/living/carbon/human/species_types/anthropomorph.dm b/code/modules/mob/living/carbon/human/species_types/anthropomorph.dm
index df129c6ff4..334f76c487 100644
--- a/code/modules/mob/living/carbon/human/species_types/anthropomorph.dm
+++ b/code/modules/mob/living/carbon/human/species_types/anthropomorph.dm
@@ -24,11 +24,18 @@
id = SPECIES_MAMMAL_SYNTHETIC
species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE,WINGCOLOR,HORNCOLOR)
- inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM)
+ inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM, TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_AUXILIARY_LUNGS)
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID|MOB_BEAST
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
+
+ coldmod = 0.5
+ heatmod = 1.2
+ cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
+ blacklisted_quirks = list(/datum/quirk/coldblooded)
+ balance_point_values = TRUE
+
//Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
@@ -47,5 +54,7 @@
attack_sound = 'sound/weapons/slash.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
+ exotic_bloodtype = "S"
+ exotic_blood_color = BLOOD_COLOR_OIL
allowed_limb_ids = list("mammal","aquatic","avian", "human")
species_category = "robot"
diff --git a/code/modules/mob/living/carbon/human/species_types/ipc.dm b/code/modules/mob/living/carbon/human/species_types/ipc.dm
index eb870f9624..4f6b940692 100644
--- a/code/modules/mob/living/carbon/human/species_types/ipc.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ipc.dm
@@ -5,7 +5,7 @@
default_color = "00FF00"
blacklisted = 0
sexes = 0
- inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM)
+ inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM, TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_AUXILIARY_LUNGS)
species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE,HAIR,ROBOTIC_LIMBS)
hair_alpha = 210
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
@@ -13,6 +13,12 @@
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
+ coldmod = 0.5
+ heatmod = 1.2
+ cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
+ blacklisted_quirks = list(/datum/quirk/coldblooded)
+ balance_point_values = TRUE
+
//Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
@@ -26,7 +32,7 @@
//special cybernetic organ for getting power from apcs
mutant_organs = list(/obj/item/organ/cyberimp/arm/power_cord)
- exotic_bloodtype = "HF"
+ exotic_bloodtype = "S"
exotic_blood_color = BLOOD_COLOR_OIL
species_category = SPECIES_CATEGORY_ROBOT
diff --git a/code/modules/mob/living/carbon/human/species_types/synthliz.dm b/code/modules/mob/living/carbon/human/species_types/synthliz.dm
index b6a7e93c72..7fc2da1132 100644
--- a/code/modules/mob/living/carbon/human/species_types/synthliz.dm
+++ b/code/modules/mob/living/carbon/human/species_types/synthliz.dm
@@ -4,11 +4,18 @@
say_mod = "beeps"
default_color = "00FF00"
species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE)
- inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM)
+ inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM, TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_AUXILIARY_LUNGS)
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
mutant_bodyparts = list("ipc_antenna" = "Synthetic Lizard - Antennae","mam_tail" = "Synthetic Lizard", "mam_snouts" = "Synthetic Lizard - Snout", "legs" = "Digitigrade", "mam_body_markings" = "Synthetic Lizard - Plates", "taur" = "None")
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
+
+ coldmod = 0.5
+ heatmod = 1.2
+ cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
+ blacklisted_quirks = list(/datum/quirk/coldblooded)
+ balance_point_values = TRUE
+
//Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 28c5ca5334..9cc96f2ddd 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -659,7 +659,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
//used in human and monkey handle_environment()
/mob/living/carbon/proc/natural_bodytemperature_stabilization()
- if (HAS_TRAIT(src, TRAIT_COLDBLOODED))
+ if(HAS_TRAIT(src, TRAIT_COLDBLOODED) || HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
return 0 //Return 0 as your natural temperature. Species proc handle_environment() will adjust your temperature based on this.
var/body_temperature_difference = BODYTEMP_NORMAL - bodytemperature
@@ -672,6 +672,49 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
return min(body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR, max(body_temperature_difference, -BODYTEMP_AUTORECOVERY_MINIMUM/4))
if(BODYTEMP_HEAT_DAMAGE_LIMIT to INFINITY)
return min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers
+
+/mob/living/carbon/proc/get_cooling_efficiency()
+ if(!HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
+ return 1
+
+ var/integration_bonus = min(blood_volume * SYNTH_INTEGRATION_COOLANT_CAP, integrating_blood * SYNTH_INTEGRATION_COOLANT_PENALTY) //Integration blood somewhat helps, though only at 40% impact and to a cap of 25% of current blood level.
+ var/blood_effective_volume = blood_volume + integration_bonus
+ var/coolant_efficiency = min(blood_effective_volume / BLOOD_VOLUME_SAFE, 1) //Low coolant is only a negative, adding more than needed will not help you.
+ var/environment_efficiency = get_environment_cooling_efficiency()
+
+ return min(coolant_efficiency * environment_efficiency, SYNTH_MAX_COOLING_EFFICIENCY)
+
+
+/mob/living/carbon/proc/get_environment_cooling_efficiency()
+ var/suitlink = check_suitlinking()
+ if(suitlink)
+ return suitlink //If you are wearing full EVA or lavaland hazard gear (on lavaland), assume it has been made to accomodate your cooling needs.
+ var/datum/gas_mixture/environment = loc.return_air()
+ if(!environment)
+ return 0
+
+ var/pressure = environment.return_pressure()
+ var/heat = environment.return_temperature()
+
+ var/heat_efficiency = clamp(1 + ((bodytemperature - heat) * SYNTH_HEAT_EFFICIENCY_COEFF), 0, SYNTH_SINGLE_INFLUENCE_COOLING_EFFECT_CAP)
+ var/pressure_efficiency = clamp(pressure / ONE_ATMOSPHERE, 0, SYNTH_SINGLE_INFLUENCE_COOLING_EFFECT_CAP)
+
+ var/total_environment_efficiency = min(heat_efficiency * pressure_efficiency, SYNTH_TOTAL_ENVIRONMENT_EFFECT_CAP) //At best, you can get 200% total
+ return total_environment_efficiency
+
+/mob/living/carbon/proc/check_suitlinking()
+ var/suit_item = get_item_by_slot(SLOT_WEAR_SUIT)
+ var/head_item = get_item_by_slot(SLOT_HEAD)
+ var/turf/T = get_turf(src)
+
+ if(istype(head_item, /obj/item/clothing/head/helmet/space) && istype(suit_item, /obj/item/clothing/suit/space))
+ return 1
+
+ if(T && is_mining_level(T.z) && istype(head_item, /obj/item/clothing/head/hooded/explorer) && istype(suit_item, /obj/item/clothing/suit/hooded/explorer))
+ return 1
+
+ return 0
+
/////////
//LIVER//
/////////
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 650409b5cc..a47ac494bf 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -17,7 +17,6 @@
/datum/reagent/medicine/leporazine
name = "Leporazine"
description = "Leporazine will effectively regulate a patient's body temperature, ensuring it never leaves safe levels."
- chemical_flags = REAGENT_ALL_PROCESS
pH = 8.4
color = "#82b8aa"
value = REAGENT_VALUE_COMMON
@@ -693,7 +692,6 @@
reagent_state = LIQUID
color = "#00FFFF"
metabolization_rate = 0.25 * REAGENTS_METABOLISM
- chemical_flags = REAGENT_ALL_PROCESS
pH = 2
/datum/reagent/medicine/salbutamol/on_mob_life(mob/living/carbon/M)
@@ -710,7 +708,6 @@
reagent_state = LIQUID
color = "#FF6464"
metabolization_rate = 0.25 * REAGENTS_METABOLISM
- chemical_flags = REAGENT_ALL_PROCESS
pH = 11
/datum/reagent/medicine/perfluorodecalin/on_mob_life(mob/living/carbon/human/M)
@@ -920,7 +917,6 @@
reagent_state = LIQUID
color = "#000000"
metabolization_rate = 0.25 * REAGENTS_METABOLISM
- chemical_flags = REAGENT_ALL_PROCESS
overdose_threshold = 35
pH = 12
value = REAGENT_VALUE_UNCOMMON
@@ -951,7 +947,6 @@
reagent_state = LIQUID
color = "#D2FFFA"
metabolization_rate = 0.25 * REAGENTS_METABOLISM
- chemical_flags = REAGENT_ALL_PROCESS
overdose_threshold = 30
pH = 10.2
@@ -1203,7 +1198,6 @@
description = "Restores oxygen loss. Overdose causes it instead."
reagent_state = LIQUID
color = "#13d2f0"
- chemical_flags = REAGENT_ALL_PROCESS
overdose_threshold = 30
pH = 9.7
@@ -1267,7 +1261,6 @@
reagent_state = LIQUID
pH = 8.5
color = "#5dc1f0"
- chemical_flags = REAGENT_ALL_PROCESS
/datum/reagent/medicine/inaprovaline/on_mob_life(mob/living/carbon/M)
if(M.losebreath >= 5)
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 663cf1fdd7..cf17ec8545 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -213,7 +213,7 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
-/datum/design/hypospray/mkii
+/datum/design/hypospray_mkii
name = "Hypospray Mk. II"
id = "hypospray_mkii"
build_type = PROTOLATHE
@@ -222,6 +222,15 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+/datum/design/nanogel
+ name = "Nanogel paste"
+ id = "nanogel"
+ build_type = PROTOLATHE | MECHFAB
+ materials = list(/datum/material/iron = 800, /datum/material/titanium = 500, /datum/material/gold = 100, /datum/material/diamond = 20)
+ build_path = /obj/item/stack/medical/nanogel/one
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
/datum/design/blood_bag
name = "Empty Blood Bag"
desc = "A small sterilized plastic bag for blood."
diff --git a/code/modules/research/techweb/nodes/robotics_nodes.dm b/code/modules/research/techweb/nodes/robotics_nodes.dm
index 6248cd99aa..ee4fbd261b 100644
--- a/code/modules/research/techweb/nodes/robotics_nodes.dm
+++ b/code/modules/research/techweb/nodes/robotics_nodes.dm
@@ -28,7 +28,7 @@
display_name = "Advanced Robotics Research"
description = "It can even do the dishes!"
prereq_ids = list("robotics")
- design_ids = list("borg_upgrade_diamonddrill", "borg_upgrade_advancedmop", "borg_upgrade_advcutter")
+ design_ids = list("borg_upgrade_diamonddrill", "borg_upgrade_advancedmop", "borg_upgrade_advcutter", "nanogel")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3000)
/datum/techweb_node/neural_programming
diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm
index 3787d4e781..80b7599ec3 100644
--- a/code/modules/surgery/organs/ears.dm
+++ b/code/modules/surgery/organs/ears.dm
@@ -148,14 +148,14 @@
return
to_chat(owner, "Alert: Auditory systems corrupted!.")
switch(severity)
- if(1)
- owner.Jitter(30)
- owner.Dizzy(30)
- owner.DefaultCombatKnockdown(80)
- deaf = 30
-
- if(2)
+ if(1 to 50)
owner.Jitter(15)
owner.Dizzy(15)
owner.DefaultCombatKnockdown(40)
+
+ if(50 to INFINITY)
+ owner.Jitter(30)
+ owner.Dizzy(30)
+ owner.DefaultCombatKnockdown(80)
+ deaf = max(deaf, 30)
damage += 0.15 * severity
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index cbb46242c3..f6265969e7 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -415,7 +415,7 @@
if(!.)
return
if(!failed && organ_flags & ORGAN_FAILING)
- if(owner && owner.stat == CONSCIOUS)
+ if(owner && owner.stat == CONSCIOUS && !HAS_TRAIT(owner, TRAIT_NOBREATH))
owner.visible_message("[owner] grabs [owner.p_their()] throat, struggling for breath!", \
"You suddenly feel like you can't breathe!")
failed = TRUE
@@ -425,6 +425,10 @@
/obj/item/organ/lungs/ipc
name = "ipc cooling system"
icon_state = "lungs-c"
+ var/is_cooling = 0
+ var/cooling_coolant_drain = 5 //Coolant (blood) use per tick of active cooling.
+ var/next_warn = BLOOD_VOLUME_NORMAL
+ actions_types = list(/datum/action/item_action/organ_action/toggle)
/obj/item/organ/lungs/ipc/emp_act(severity) //Should probably put it somewhere else later
. = ..()
@@ -432,11 +436,65 @@
return
to_chat(owner, "Alert: Critical cooling system failure!")
switch(severity)
- if(1)
- owner.adjust_bodytemperature(100*TEMPERATURE_DAMAGE_COEFFICIENT)
- if(2)
+ if(1 to 50)
owner.adjust_bodytemperature(30*TEMPERATURE_DAMAGE_COEFFICIENT)
+ if(50 to INFINITY)
+ owner.adjust_bodytemperature(100*TEMPERATURE_DAMAGE_COEFFICIENT)
+
+/obj/item/organ/lungs/ipc/ui_action_click(mob/user, actiontype)
+ if(!owner)
+ return
+ if(!HAS_TRAIT(user, TRAIT_ROBOTIC_ORGANISM))
+ to_chat(user, "Biotype incompatible with cooling system. Activation signal suppressed.")
+ return
+ if(!is_cooling && owner.blood_volume < cooling_coolant_drain)
+ to_chat(user, "Coolant levels insufficient to enable active cooling - Replenish immediately.")
+ return
+ is_cooling = !is_cooling
+ to_chat(user, "Active cooling [is_cooling ? "enabled" : "disabled"] - current coolant level: [round(owner.blood_volume / BLOOD_VOLUME_NORMAL * 100, 0.1)] percent.")
+ var/possible_next_warn = owner.blood_volume - (BLOOD_VOLUME_NORMAL * 0.1)
+ if(possible_next_warn > next_warn)
+ next_warn = possible_next_warn //If we recovered blood inbetween activations, update warning
+
+/obj/item/organ/lungs/ipc/on_life(seconds, times_fired)
+ . = ..()
+ if(!.)
+ if(is_cooling)
+ to_chat(owner, "Cooling system safeguards triggered - active cooling aborted.")
+ is_cooling = 0
+ return
+ if(!is_cooling)
+ return
+ if(!HAS_TRAIT(owner, TRAIT_ROBOTIC_ORGANISM))
+ to_chat(owner, "Biotype incompatible with cooling system. Commencing emergency shutdown.")
+ is_cooling = 0
+ return
+ if(owner.stat >= SOFT_CRIT)
+ to_chat(owner, "Operating system ping returned null response - Shutting down active cooling to avoid component damage.")
+ is_cooling = 0
+ return
+ if(owner.blood_volume < cooling_coolant_drain)
+ to_chat(owner, "Coolant levels insufficient to maintain active cooling - Replenish immediately.")
+ is_cooling = 0
+ return
+ if(abs(owner.bodytemperature - T20C) < SYNTH_ACTIVE_COOLING_TEMP_BOUNDARY)
+ return //Does not drain coolant (blood) nor do anything if we are close enough to room temp.
+ var/cooling_efficiency = owner.get_cooling_efficiency()
+ var/actual_drain = cooling_coolant_drain * max(1 - cooling_efficiency, 0.2) //Being in a suitable environment reduces drain by up to 80%
+ var/temp_diff = owner.bodytemperature - T20C
+ if(temp_diff > 0)
+ owner.adjust_bodytemperature(clamp(((T0C - owner.bodytemperature) * max(cooling_efficiency, 0.5) / BODYTEMP_COLD_DIVISOR), BODYTEMP_COOLING_MAX, -SYNTH_ACTIVE_COOLING_MIN_ADJUSTMENT))
+ else
+ owner.adjust_bodytemperature(clamp(((T20C - owner.bodytemperature) * max(cooling_efficiency, 0.5) / BODYTEMP_HEAT_DIVISOR), SYNTH_ACTIVE_COOLING_MIN_ADJUSTMENT, BODYTEMP_HEATING_MAX))
+ var/datum/gas_mixture/air = owner.loc.return_air()
+ if(!air || air.return_pressure() < ONE_ATMOSPHERE * SYNTH_ACTIVE_COOLING_LOW_PRESSURE_THRESHOLD)
+ actual_drain *= SYNTH_ACTIVE_COOLING_LOW_PRESSURE_PENALTY //Our cooling system can handle hot places okayish, but starts to cry at low pressures (reads: Effectively vents hot coolant thats been warmed up via internal heat-exchange as emergency measure and with very low efficiency)
+ owner.blood_volume = max(owner.blood_volume - actual_drain, 0)
+ if(owner.blood_volume <= next_warn)
+ to_chat(owner, "[owner.blood_volume > BLOOD_VOLUME_BAD ? "" : ""]Coolant level passed threshold - now [round(owner.blood_volume / BLOOD_VOLUME_NORMAL * 100, 0.1)] percent.")
+ next_warn -= (BLOOD_VOLUME_NORMAL * 0.1)
+
/obj/item/organ/lungs/plasmaman
name = "plasma filter"
desc = "A spongy rib-shaped mass for filtering plasma from the air."
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index de8e3d623d..a3c6ea06c8 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -249,7 +249,7 @@
var/breathes = TRUE
var/blooded = TRUE
if(dna && dna.species)
- if(HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT))
+ if(!HAS_TRAIT_FROM(src, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) && HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT))
breathes = FALSE
if(NOBLOOD in dna.species.species_traits)
blooded = FALSE
diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm
index f436b31513..f7b84eab20 100644
--- a/code/modules/surgery/organs/stomach.dm
+++ b/code/modules/surgery/organs/stomach.dm
@@ -136,11 +136,11 @@
if(!owner || . & EMP_PROTECT_SELF)
return
switch(severity)
- if(1)
- owner.nutrition = min(owner.nutrition - 50, 0)
+ if(1 to 50)
+ owner.nutrition = max(owner.nutrition - 50, 0)
to_chat(owner, "Alert: Detected severe battery discharge!")
- if(2)
- owner.nutrition = min(owner.nutrition - 100, 0)
+ if(50 to INFINITY)
+ owner.nutrition = max(owner.nutrition - 100, 0)
to_chat(owner, "Alert: Minor battery discharge!")
/obj/item/organ/stomach/ethereal
diff --git a/icons/mob/screen_synth.dmi b/icons/mob/screen_synth.dmi
new file mode 100644
index 0000000000..95ecc5e2bb
Binary files /dev/null and b/icons/mob/screen_synth.dmi differ