Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Semi-Experimental] The coldbloodening #2684

Merged
merged 10 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion code/_globalvars/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 13 additions & 14 deletions code/modules/mob/living/carbon/human/species.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 4 additions & 0 deletions code/modules/mob/living/carbon/life.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Bokkiewokkie marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand Down
3 changes: 3 additions & 0 deletions code/modules/mob/living/life.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions nsv13.dme
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
17 changes: 17 additions & 0 deletions nsv13/code/__DEFINES/atmospherics.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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.4 //! Standard use for temp adjustment for ectotherms.
#define ECTOTHERM_MAJOR_THERMOGENESIS_NUTRITION_USE 2 //! 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.
6 changes: 5 additions & 1 deletion nsv13/code/datums/mood_events/nsv_events.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/datum/mood_event/moth_drink_blood
description = "<span class='nicegreen'>That hit the spot!</span>\n"
mood_change = 10
mood_change = 10 //Why is a moth drinking blood a mood buff the level of being a cultist?? And for *ten minutes*???
timeout = 10 MINUTES
Bokkiewokkie marked this conversation as resolved.
Show resolved Hide resolved

/datum/mood_event/tailpull
Expand Down Expand Up @@ -52,3 +52,7 @@
description = "<span class='nicegreen'>Cheers! ¡Salud! Kanpai! Prost! Skål! Santé! Sláinte! Saúde!</span>\n"
mood_change = 3
timeout = 30 SECONDS

/datum/mood_event/lizard_vibrations
description = "<span class='warning'>I know my muscles are shaking to warm me up, but it still feels distracting..</span>\n"
mood_change = -2
Bokkiewokkie marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 4 additions & 0 deletions nsv13/code/modules/mob/living/carbon/human/human.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
39 changes: 39 additions & 0 deletions nsv13/code/modules/mob/living/carbon/human/nsv_species.dm
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//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

//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, "<span class='warning'>Various muscles across your body start quivering!</span>")
SEND_SIGNAL(human_holder, COMSIG_ADD_MOOD_EVENT, "lizard_trembles", /datum/mood_event/lizard_vibrations)
fibrillating = TRUE
else if(cold_stacks == 0) //We also check if we have to stop vibrating here.
to_chat(human_holder, "<span class='notice'>Your muscles calm down.</span>")
SEND_SIGNAL(human_holder, COMSIG_CLEAR_MOOD_EVENT, "lizard_trembles")
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_trembles") //Safely remove if our species is changed.
return ..()
46 changes: 46 additions & 0 deletions nsv13/code/modules/mob/living/nsv_life.dm
Original file line number Diff line number Diff line change
@@ -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, abs(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 = abs(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, abs(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
Loading