From 5f1c555177295c70dc1b783500ee6f72c0677abc Mon Sep 17 00:00:00 2001
From: DeltaFire <46569814+DeltaFire15@users.noreply.github.com>
Date: Fri, 13 Sep 2024 16:46:38 +0200
Subject: [PATCH] [Semi-Experimental] The coldbloodening (#2684)
---
code/__DEFINES/traits.dm | 2 +
code/_globalvars/traits.dm | 3 +-
code/modules/mob/living/carbon/human/life.dm | 4 +-
.../mob/living/carbon/human/species.dm | 27 ++++----
code/modules/mob/living/carbon/life.dm | 4 ++
code/modules/mob/living/life.dm | 3 +
nsv13.dme | 3 +
nsv13/code/__DEFINES/atmospherics.dm | 17 +++++
nsv13/code/datums/mood_events/nsv_events.dm | 18 +++--
.../modules/mob/living/carbon/human/human.dm | 4 ++
.../mob/living/carbon/human/nsv_species.dm | 39 +++++++++++
.../human/species_types/nsv_lizardpeople.dm | 67 +++++++++++++++++++
nsv13/code/modules/mob/living/nsv_life.dm | 46 +++++++++++++
13 files changed, 215 insertions(+), 22 deletions(-)
create mode 100644 nsv13/code/modules/mob/living/carbon/human/nsv_species.dm
create mode 100644 nsv13/code/modules/mob/living/carbon/human/species_types/nsv_lizardpeople.dm
create mode 100644 nsv13/code/modules/mob/living/nsv_life.dm
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 7b3ccedb781..801955563f1 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -241,6 +241,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_SOMMELIER "sommelier" // shows different booze power flavor texts
#define TRAIT_BARMASTER "bar_master" // always can identify reagents
#define TRAIT_HIVE_BURNT "hive-burnt"
+///Prevents natural body temperature stabilization.
+#define TRAIT_COLDBLOODED "cold_blooded" //NSV13 species trait.
// You can stare into the abyss, but it does not stare back.
// You're immune to the hallucination effect of the supermatter, either
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 952feff6854..5299a5d4bd6 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -110,7 +110,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT,
"TRAIT_ALWAYS_STUBS" = TRAIT_ALWAYS_STUBS,
"TRAIT_NAIVE" = TRAIT_NAIVE,
- "TRAIT_DROPS_ITEMS_ON_DEATH" = TRAIT_DROPS_ITEMS_ON_DEATH
+ "TRAIT_DROPS_ITEMS_ON_DEATH" = TRAIT_DROPS_ITEMS_ON_DEATH,
+ "TRAIT_COLDBLOODED" = TRAIT_COLDBLOODED//NSV13
),
/obj/item/bodypart = list(
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 32758526664..1aad4689f04 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -245,7 +245,7 @@
thermal_protection += THERMAL_PROTECTION_HAND_RIGHT
- return min(1,thermal_protection)
+ return min(1,round(thermal_protection, 0.001)) //NSV13 - rounding because sure, lets split this into 11 values, decimal precision will NOT mess us up :)
//See proc/get_heat_protection_flags(temperature) for the description of this proc.
/mob/living/carbon/human/proc/get_cold_protection_flags(temperature)
@@ -302,7 +302,7 @@
if(thermal_protection_flags & HAND_RIGHT)
thermal_protection += THERMAL_PROTECTION_HAND_RIGHT
- return min(1,thermal_protection)
+ return min(1,round(thermal_protection, 0.001)) //NSV13 - rounding because sure, lets split this into 11 values, decimal precision will NOT mess us up :)
/mob/living/carbon/human/handle_random_events()
//Puke if toxloss is too high
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index aecc0b67dd0..7318e3ec06a 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1883,27 +1883,26 @@ GLOBAL_LIST_EMPTY(roundstart_races)
//Body temperature is adjusted in two parts: first there your body tries to naturally preserve homeostasis (shivering/sweating), then it reacts to the surrounding environment
//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
- if(H.stat != DEAD)
- natural = H.natural_bodytemperature_stabilization()
+
+ //NSV13 - segment adjusted due to jank.
+ var/natural = 0
+ if(H.stat != DEAD)
+ natural = H.natural_bodytemperature_stabilization()
+ if(!H.on_fire && loc_temp < H.bodytemperature) //Place is colder than we are. But don't cool if we are on fire.
var/thermal_protection = 1
- if(loc_temp < H.bodytemperature) //Place is colder than we are
- thermal_protection -= H.get_cold_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) //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))
- 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
- if (loc_temp > H.bodytemperature) //Place is hotter than we are
- var/natural = 0
- if(H.stat != DEAD)
- natural = H.natural_bodytemperature_stabilization()
+ thermal_protection -= H.get_cold_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) //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))
+ 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
+ else if(loc_temp > H.bodytemperature) //Place is hotter than we are
var/thermal_protection = 1
thermal_protection -= H.get_heat_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
H.adjust_bodytemperature((thermal_protection+1)*natural + min(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
else //we're sweating, insulation hinders out ability to reduce heat - but will reduce the amount of heat we get from the environment
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + min(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
+ //NSV13 end.
// +/- 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))
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 09401524726..aea243a733b 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -547,6 +547,10 @@ 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()
+ //NSV13 - coldblooded beings do not naturally stabilize.
+ if(HAS_TRAIT(src, TRAIT_COLDBLOODED))
+ return 0
+ //NSV13 end.
var/body_temperature_difference = BODYTEMP_NORMAL - bodytemperature
switch(bodytemperature)
if(-INFINITY to BODYTEMP_COLD_DAMAGE_LIMIT) //Cold damage limit is 50 below the default, the temperature where you start to feel effects.
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index e12943185f7..ce543b9fe6a 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -33,6 +33,9 @@
var/datum/gas_mixture/environment = loc.return_air()
if(environment)
handle_environment(environment)
+ ///NSV13 - aggressive grab temp exchange hook.
+ handle_temperature_exchange()
+ ///NSV13 end.
//Handle gravity
var/gravity = has_gravity()
diff --git a/nsv13.dme b/nsv13.dme
index 8806afaff65..97b9c981cf1 100644
--- a/nsv13.dme
+++ b/nsv13.dme
@@ -3933,13 +3933,16 @@
#include "nsv13\code\modules\mob\mob_helpers.dm"
#include "nsv13\code\modules\mob\dead\new_player\sprite_accessories.dm"
#include "nsv13\code\modules\mob\dead\observer\oberserver.dm"
+#include "nsv13\code\modules\mob\living\nsv_life.dm"
#include "nsv13\code\modules\mob\living\carbon\carbon.dm"
#include "nsv13\code\modules\mob\living\carbon\examine_tgui.dm"
#include "nsv13\code\modules\mob\living\carbon\human\human.dm"
#include "nsv13\code\modules\mob\living\carbon\human\nsv_emotes.dm"
#include "nsv13\code\modules\mob\living\carbon\human\nsv_human_helpers.dm"
+#include "nsv13\code\modules\mob\living\carbon\human\nsv_species.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\catgirl.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\nanotrasen_knpc.dm"
+#include "nsv13\code\modules\mob\living\carbon\human\species_types\nsv_lizardpeople.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\other_knpc.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\spacepirate_knpc.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\syndicate_knpc.dm"
diff --git a/nsv13/code/__DEFINES/atmospherics.dm b/nsv13/code/__DEFINES/atmospherics.dm
index e12f9c12bc1..2cdd8dadd87 100644
--- a/nsv13/code/__DEFINES/atmospherics.dm
+++ b/nsv13/code/__DEFINES/atmospherics.dm
@@ -2,3 +2,20 @@
#define ATMOS_GAS_MONITOR_OUTPUT_NUCLEIUM "nucleium_out"
#define ATMOS_GAS_MONITOR_SENSOR_NUCLEIUM "nucleium_sensor"
#define ATMOS_TANK_NUCLEIUM "nucleium=750;TEMP=293.15"
+
+//Nerd stuff (coldblooded limited thermoregulation via e.g. muscle fibrillations)
+
+#define LIZARD_ECTOTHERMISM_COLD_MAX_STACKS 10 //! Essentially the maximum life ticks thermogenesis will remain ready / active after the condition has passed.
+#define LIZARD_THERMOGENESIS_COLD_TRIGGER_STACKS 5 //! At this many stacks we start using thermogenesis.
+
+#define ECTOTHERM_THERMOGENESIS_MIN_COLDNESS 22 //! An ectotherm humanoid must be at least this many kelvin below the normal bodytemperature target define to actively thermoregulate.
+#define ECTOTHERM_THERMOGENESIS_CRIT_COLDNESS 50 //! If this much temperature difference exists (relative to the basic standard temp), we are VERY cold and use a significant portion of energy just to vibrate.
+#define ECTOTHERM_RECOVERY_DIVISOR 11 //! The base divisor for total heat difference that is tried to adjust for. For now just the base autorecovery divisor since it usually hits the min or max cap.
+#define ECTOTHERM_MIN_RECOVERY 0.2 //! The minimum amount of heat (kelvins) generated by ectotherms if trying to recover. Should be very small
+#define ECTOTHERM_MAX_RECOVERY 3 //! The maximum amount of heat (kelvins) generated per tick by ectotherms if trying to recover. Should be fairly small.
+#define ECTOTHERM_CRIT_COLD_MAX_RECOVERY_MOD 2 //! Maximum temperature recovery is multiplied by this value if we are below the critical coldness threshold.
+
+#define ECTOTHERM_THERMOGENESIS_NUTRITION_USE 0.2 //! Standard use for temp adjustment for ectotherms.
+#define ECTOTHERM_MAJOR_THERMOGENESIS_NUTRITION_USE 1 //! This method of temperature regulation is inefficient, thus its nutrition drain increases disproportionately to the cap gained if very cold.
+
+#define ECTOTHERM_NO_THERMOGENESIS_NEEDED -1 //! Return value if no thermoregulation was needed nor done.
diff --git a/nsv13/code/datums/mood_events/nsv_events.dm b/nsv13/code/datums/mood_events/nsv_events.dm
index d684925ae08..8ca78dc5b70 100644
--- a/nsv13/code/datums/mood_events/nsv_events.dm
+++ b/nsv13/code/datums/mood_events/nsv_events.dm
@@ -1,7 +1,7 @@
/datum/mood_event/moth_drink_blood
description = "That hit the spot!\n"
- mood_change = 10
- timeout = 10 MINUTES
+ mood_change = 3
+ timeout = 7 MINUTES
/datum/mood_event/tailpull
description = "OUCH! Stop pulling my tail! It hurts!\n"
@@ -40,11 +40,11 @@
/datum/mood_event/drink_navy_coffee
description = "THAT SHIT TASTED FUCKING DELICIOUS LET'S GO FUCK SOME SYNDICATE SHIPS UP, NAVY FOR LIFE WOOOOOO!!\n"
- mood_change = 10
- timeout = 10 MINUTES
+ mood_change = 3
+ timeout = 7 MINUTES
/datum/mood_event/drink_navy_coffee/add_effects(list/faction)
- if("Syndicate" in faction)
+ if(FACTION_SYNDICATE in faction)
description = "THAT SHIT TASTED FUCKING DELICIOUS LET'S GO FUCK SOME NANOTRASEN SHIPS UP, NAVY FOR LIFE WOOOOOO!!\n"
@@ -52,3 +52,11 @@
description = "Cheers! ¡Salud! Kanpai! Prost! Skål! Santé! Sláinte! Saúde!\n"
mood_change = 3
timeout = 30 SECONDS
+
+/datum/mood_event/lizard_shivers
+ description = "I'm shivering.. I need to find a spot where I can bask in the sun!\n" //Evolved mental response, even if not entirely true here.
+ mood_change = -2
+
+/datum/mood_event/comfy_lizard_temperature
+ description = "I'm nice and warm! I missed this feeling..\n" //These ships run at 20°C by default, which is.. not very nice for something coldblooded.
+ mood_change = 2 //This is really hard to hit and maintain so I felt like at least a +2 would be appropriate.
diff --git a/nsv13/code/modules/mob/living/carbon/human/human.dm b/nsv13/code/modules/mob/living/carbon/human/human.dm
index 9a1b61d44fa..bf9d1e28764 100644
--- a/nsv13/code/modules/mob/living/carbon/human/human.dm
+++ b/nsv13/code/modules/mob/living/carbon/human/human.dm
@@ -9,3 +9,7 @@
if(gravity <= 1) //This is fine.
return
return ..()
+
+//OVERRIDE - Humans handle thermoregulation on species level.
+/mob/living/carbon/human/natural_bodytemperature_stabilization()
+ return dna.species.natural_bodytemperature_stabilization(src)
diff --git a/nsv13/code/modules/mob/living/carbon/human/nsv_species.dm b/nsv13/code/modules/mob/living/carbon/human/nsv_species.dm
new file mode 100644
index 00000000000..3214ec2cde5
--- /dev/null
+++ b/nsv13/code/modules/mob/living/carbon/human/nsv_species.dm
@@ -0,0 +1,39 @@
+//Modular File for NSV species stuff
+
+///The species-level version of bodytemperature stabilization.
+/datum/species/proc/natural_bodytemperature_stabilization(mob/living/carbon/human/human_holder)
+ if(HAS_TRAIT(human_holder, TRAIT_COLDBLOODED))
+ ectotherm_thermogenesis(human_holder) //Man I love the word "thermogenesis". Such a magic term for what is essentially just "makes heat".
+ return 0
+ var/body_temperature_difference = BODYTEMP_NORMAL - human_holder.bodytemperature
+ switch(human_holder.bodytemperature)
+ if(-INFINITY to BODYTEMP_COLD_DAMAGE_LIMIT) //Cold damage limit is 50 below the default, the temperature where you start to feel effects.
+ return max((body_temperature_difference * human_holder.metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM)
+ if(BODYTEMP_COLD_DAMAGE_LIMIT to BODYTEMP_NORMAL)
+ return max(body_temperature_difference * human_holder.metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR, min(body_temperature_difference, BODYTEMP_AUTORECOVERY_MINIMUM/4))
+ if(BODYTEMP_NORMAL to BODYTEMP_HEAT_DAMAGE_LIMIT) // Heat damage limit is 50 above the default, the temperature where you start to feel effects.
+ return min(body_temperature_difference * human_holder.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
+
+/**
+ * A proc for coldblooded species' means of limited thermal control. Also known as "vibrating your muscles".
+ * * Returns: Amount of kelvin adjustment performed, or ECTOTHERM_NO_THERMOGENESIS_NEEDED (-1) if we are fine.
+**/
+/datum/species/proc/ectotherm_thermogenesis(mob/living/carbon/human/human_holder, use_temp_diff_range_check = TRUE)
+ var/temperature_differential = BODYTEMP_NORMAL - human_holder.bodytemperature
+ if(temperature_differential <= 0 || (use_temp_diff_range_check && temperature_differential < ECTOTHERM_THERMOGENESIS_MIN_COLDNESS))
+ return ECTOTHERM_NO_THERMOGENESIS_NEEDED
+ var/adjustment = 0
+ if(temperature_differential < ECTOTHERM_THERMOGENESIS_CRIT_COLDNESS)
+ if(human_holder.nutrition < ECTOTHERM_THERMOGENESIS_NUTRITION_USE * human_holder.metabolism_efficiency)
+ return 0
+ adjustment = round(CLAMP(temperature_differential / ECTOTHERM_RECOVERY_DIVISOR * human_holder.metabolism_efficiency, ECTOTHERM_MIN_RECOVERY * human_holder.metabolism_efficiency, ECTOTHERM_MAX_RECOVERY * human_holder.metabolism_efficiency), 0.1)
+ human_holder.adjust_nutrition(-ECTOTHERM_THERMOGENESIS_NUTRITION_USE * human_holder.metabolism_efficiency)
+ else
+ if(human_holder.nutrition < ECTOTHERM_MAJOR_THERMOGENESIS_NUTRITION_USE * human_holder.metabolism_efficiency)
+ return 0
+ adjustment = round(CLAMP(temperature_differential / ECTOTHERM_RECOVERY_DIVISOR * human_holder.metabolism_efficiency, ECTOTHERM_MIN_RECOVERY * human_holder.metabolism_efficiency, ECTOTHERM_MAX_RECOVERY * human_holder.metabolism_efficiency * ECTOTHERM_CRIT_COLD_MAX_RECOVERY_MOD), 0.1)
+ human_holder.adjust_nutrition(-ECTOTHERM_MAJOR_THERMOGENESIS_NUTRITION_USE * human_holder.metabolism_efficiency)
+ human_holder.adjust_bodytemperature(adjustment)
+ return adjustment
diff --git a/nsv13/code/modules/mob/living/carbon/human/species_types/nsv_lizardpeople.dm b/nsv13/code/modules/mob/living/carbon/human/species_types/nsv_lizardpeople.dm
new file mode 100644
index 00000000000..360c469fd23
--- /dev/null
+++ b/nsv13/code/modules/mob/living/carbon/human/species_types/nsv_lizardpeople.dm
@@ -0,0 +1,67 @@
+//Modular NSV file for special stuff lizards have here.
+
+//Modular type attachment.
+/datum/species/lizard
+ coldmod = 1 // Lizards here have exchanged their inherent damage modifier for them being cold-blooded.
+ inherent_traits = list(TRAIT_COLDBLOODED) // The aforementioned coldbloodedness
+ ///Controls whether lizards use their muscles to generate additional heat if very cold.
+ var/cold_stacks = 0
+ ///Stores if we already sent them a message & adjusted stuff.
+ var/fibrillating = FALSE
+ ///Used to store whether the signal to adjust mood due to good temperatures has been updated.
+ var/is_comfy = FALSE
+
+//Another modular type attachment.
+/datum/species/lizard/ashwalker
+ inherent_traits = list(TRAIT_NOGUNS,TRAIT_NOBREATH, TRAIT_COLDBLOODED) //Ashwalkers are also coldblooded [we have no lavaland so I don't have to worry about if lavaland always gens with survivable temps :) ]
+
+/datum/species/lizard/ectotherm_thermogenesis(mob/living/carbon/human/human_holder, use_temp_diff_range_check = TRUE)
+ var/temp_diff = BODYTEMP_NORMAL - human_holder.bodytemperature
+ switch(temp_diff)
+ if(ECTOTHERM_THERMOGENESIS_CRIT_COLDNESS to INFINITY) //Being extremely cold quickly triggers thermogenesis.
+ cold_stacks = min(cold_stacks + 3, LIZARD_ECTOTHERMISM_COLD_MAX_STACKS)
+ if(ECTOTHERM_THERMOGENESIS_MIN_COLDNESS to ECTOTHERM_THERMOGENESIS_CRIT_COLDNESS)
+ cold_stacks = min(cold_stacks + 1, LIZARD_ECTOTHERMISM_COLD_MAX_STACKS) //Basic cold takes some time to respond to.
+ else
+ cold_stacks = max(cold_stacks - 1, 0) //Takes a while to calm down muscles.
+
+ if(cold_stacks < LIZARD_THERMOGENESIS_COLD_TRIGGER_STACKS && !fibrillating)
+ return ECTOTHERM_NO_THERMOGENESIS_NEEDED
+ if(!fibrillating) // !fibrillating reaching this point means enough stacks exist.
+ to_chat(human_holder, "You start shivering and feel the urge to find a sunny spot!")
+ SEND_SIGNAL(human_holder, COMSIG_ADD_MOOD_EVENT, "lizard_shivers", /datum/mood_event/lizard_shivers)
+ fibrillating = TRUE
+ else if(cold_stacks == 0) //We also check if we have to stop vibrating here.
+ to_chat(human_holder, "You stop shivering.")
+ SEND_SIGNAL(human_holder, COMSIG_CLEAR_MOOD_EVENT, "lizard_shivers")
+ fibrillating = FALSE
+ return ECTOTHERM_NO_THERMOGENESIS_NEEDED
+
+ return ..(human_holder, FALSE) //We already use some fancy logic for our thermoregulation triggering so we don't use the normal temp difference check save for if we would get hot.
+
+//Modular proc attachment
+/datum/species/lizard/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) //Human variable, named C. What did they mean by this?
+ SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "lizard_shivers") //Safely remove if our species is changed.
+ SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "comfy_liz_temp")
+ return ..()
+
+//Lizards are most comfortable between 30 and 60°C. Good luck managing to stabilize at that temp, but hey if you manage to, you get a mood buff!
+#define LIZARD_COMFY_TEMP_MIN 303.15
+#define LIZARD_COMFY_TEMP_MAX 333.15 //From what I read some terran lizards have ~40-45°C as their upper targeted bounds, buut firstly these are space lizards, and secondly this is already hard enough to hit, so I extended it to 60°C.
+
+/datum/species/lizard/spec_life(mob/living/carbon/human/H)
+ . = ..()
+ var/owner_bodytemperature = H.bodytemperature
+ if(owner_bodytemperature < LIZARD_COMFY_TEMP_MIN || owner_bodytemperature > LIZARD_COMFY_TEMP_MAX) //Should be low on processing since the first condition will catch almost all the time.
+ if(!is_comfy)
+ return
+ SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "comfy_liz_temp")
+ is_comfy = FALSE
+ return
+ if(is_comfy)
+ return
+ SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "comfy_liz_temp", /datum/mood_event/comfy_lizard_temperature)
+ is_comfy = TRUE
+
+#undef LIZARD_COMFY_TEMP_MIN
+#undef LIZARD_COMFY_TEMP_MAX
diff --git a/nsv13/code/modules/mob/living/nsv_life.dm b/nsv13/code/modules/mob/living/nsv_life.dm
new file mode 100644
index 00000000000..871bd14dc1d
--- /dev/null
+++ b/nsv13/code/modules/mob/living/nsv_life.dm
@@ -0,0 +1,46 @@
+///NSV13 living life() stuff file. Modularization.
+
+#define GRAB_TEMP_EXCHANGE_MIN_DIFF 1 //If we have less than 1K difference in temp, why are we even bothering? (Might also do some weird stuff at very low differences due to decimal precision)
+
+///If grabbing aggressively (or above), exchanges temperature with the target (taking into account insulation and all that stuff we hate dealing with).
+/mob/living/proc/handle_temperature_exchange()
+
+//I'm not bothering adjusting for all the weird edge cases carbons that are not-quite-human code have, so only /human to /human does this.
+/mob/living/carbon/human/handle_temperature_exchange()
+ if(!pulling || !ishuman(pulling))
+ return
+ var/mob/living/carbon/human/pulled_human = pulling
+ if(grab_state < GRAB_AGGRESSIVE) //Handholding is not enough to share body heat.
+ return
+ var/tempdiff = pulled_human.bodytemperature - bodytemperature
+ if(abs(tempdiff) < GRAB_TEMP_EXCHANGE_MIN_DIFF)
+ return //Why are we still here..
+ var/thermoconductivity = 1
+ //We ALWAYS handle the bodytemp adjustment as a cooling action, not as a heating one. If this is too effective, it could be handled as heating instead. (cold temp adjustments allow more oomph by default)
+ if(tempdiff > 0) //Pulled is hotter than us. Cool pulled, heat us by diff.
+ //Averaging insulation of both targets, UNLESS either of the two has full thermal protection to the vector.
+ var/self_thermoprotect = get_heat_protection(pulled_human.bodytemperature)
+ var/pulled_thermoprotect = pulled_human.get_cold_protection(bodytemperature)
+ if(self_thermoprotect >= 1 || pulled_thermoprotect >= 1)
+ return //Full insulation.
+ thermoconductivity -= ((self_thermoprotect + pulled_thermoprotect) / 2) //Non-full protection, average for simplicity and to preserve effectiveness.
+ var/true_adjustment = min(thermoconductivity * tempdiff / BODYTEMP_COLD_DIVISOR, -BODYTEMP_COOLING_MAX)
+ //Aaand equalize.
+ adjust_bodytemperature(true_adjustment)
+ pulled_human.adjust_bodytemperature(-true_adjustment)
+ . = true_adjustment //This return value isn't used but someone might use it. Plus, traceability.
+ else if(tempdiff < 0) //We are hotter than pulled. Cool us, transfer lost heat to them.
+ tempdiff = -tempdiff //This was negative. I don't want that.
+ //Averaging insulation of both targets again, this time the other way around because we are hotter.
+ var/self_thermoprotect = get_cold_protection(pulled_human.bodytemperature)
+ var/pulled_thermoprotect = pulled_human.get_heat_protection(bodytemperature)
+ if(self_thermoprotect >= 1 || pulled_thermoprotect >= 1)
+ return //Full insulation
+ thermoconductivity -= ((self_thermoprotect + pulled_thermoprotect) / 2) //Bit copypasty I know but I really don't feel like making an omni-case instead.
+ var/true_adjustment = min(thermoconductivity * tempdiff / BODYTEMP_COLD_DIVISOR, -BODYTEMP_COOLING_MAX)
+ //And equalize, the other way around this time.
+ adjust_bodytemperature(-true_adjustment)
+ pulled_human.adjust_bodytemperature(true_adjustment)
+ . = true_adjustment
+
+#undef GRAB_TEMP_EXCHANGE_MIN_DIFF