diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index d3bab088c96a..84d7fe10ce6e 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -550,6 +550,10 @@ #define COMSIG_LIVING_WRITE_MEMORY "living_write_memory" #define COMPONENT_DONT_WRITE_MEMORY (1<<0) +// /mob/living/simple_animal signals +///from /mob/living/simple_animal/handle_environment() +#define COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT "simpleanimal_handle_environment" + // /mob/living/carbon signals ///from base of mob/living/carbon/soundbang_act(): (list(intensity)) diff --git a/code/datums/elements/atmos_requirements.dm b/code/datums/elements/atmos_requirements.dm new file mode 100644 index 000000000000..ae6bd40bdea9 --- /dev/null +++ b/code/datums/elements/atmos_requirements.dm @@ -0,0 +1,73 @@ +/** + * Bespoke element that deals damage to the attached mob when the atmos requirements aren't satisfied + */ +/datum/element/atmos_requirements + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + /// An assoc list of "what atmos does this mob require to survive in". + var/list/atmos_requirements + /// How much (brute) damage we take from being in unsuitable atmos. + var/unsuitable_atmos_damage + +/datum/element/atmos_requirements/Attach(datum/target, list/atmos_requirements_, unsuitable_atmos_damage_ = 5) + . = ..() + + if(!issimple_animal(target)) + return ELEMENT_INCOMPATIBLE + + if(!atmos_requirements_) + stack_trace("[type] added to [target] without any requirements specified.") + + atmos_requirements = atmos_requirements_ + unsuitable_atmos_damage = unsuitable_atmos_damage_ + RegisterSignal(target, COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT, PROC_REF(handle_environment)) + +/datum/element/atmos_requirements/Detach(datum/target) + UnregisterSignal(target, COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT) + return ..() + +/datum/element/atmos_requirements/proc/handle_environment(mob/living/simple_animal/target, datum/gas_mixture/readonly_environment) + SIGNAL_HANDLER // COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT + + if(!readonly_environment) + return + + var/atmos_suitable = TRUE + + var/tox = readonly_environment.toxins() + var/oxy = readonly_environment.oxygen() + var/n2 = readonly_environment.nitrogen() + var/co2 = readonly_environment.carbon_dioxide() + + if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) + atmos_suitable = FALSE + target.throw_alert("not_enough_oxy", /atom/movable/screen/alert/not_enough_oxy) + else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) + atmos_suitable = FALSE + target.throw_alert("too_much_oxy", /atom/movable/screen/alert/too_much_oxy) + else + target.clear_alert("not_enough_oxy") + target.clear_alert("too_much_oxy") + + if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) + atmos_suitable = FALSE + target.throw_alert("not_enough_tox", /atom/movable/screen/alert/not_enough_tox) + else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) + atmos_suitable = FALSE + target.throw_alert("too_much_tox", /atom/movable/screen/alert/too_much_tox) + else + target.clear_alert("too_much_tox") + target.clear_alert("not_enough_tox") + + if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) + atmos_suitable = FALSE + else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) + atmos_suitable = FALSE + + if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) + atmos_suitable = FALSE + else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) + atmos_suitable = FALSE + + if(!atmos_suitable) + target.adjustHealth(unsuitable_atmos_damage) diff --git a/code/datums/elements/body_temperature.dm b/code/datums/elements/body_temperature.dm new file mode 100644 index 000000000000..57a8dd7e51e6 --- /dev/null +++ b/code/datums/elements/body_temperature.dm @@ -0,0 +1,68 @@ +#define NONCARBON_DEFAULT_MIN_BODY_TEMP 250 +#define NONCARBON_DEFAULT_MAX_BODY_TEMP 350 +#define NONCARBON_DEFAULT_COLD_DAMAGE 2 +#define NONCARBON_DEFAULT_HEAT_DAMAGE 2 + +/** + * Bespoke element that deals damage to the attached mob when the ambient temperature is unsuitable. + */ +/datum/element/body_temperature + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + + /// Min body temp + var/min_body_temp = NONCARBON_DEFAULT_MIN_BODY_TEMP + /// Max body temp + var/max_body_temp = NONCARBON_DEFAULT_MAX_BODY_TEMP + + //// Damage when below min temp + var/cold_damage_per_tick = NONCARBON_DEFAULT_COLD_DAMAGE + /// Damage when above max temp + var/heat_damage_per_tick = NONCARBON_DEFAULT_HEAT_DAMAGE + +/datum/element/body_temperature/Attach(datum/target, min_body_temp_, max_body_temp_, cold_damage_per_tick_, heat_damage_per_tick_) + . = ..() + + if(!issimple_animal(target)) + return ELEMENT_INCOMPATIBLE + + if(isnum(min_body_temp)) + min_body_temp = min_body_temp_ + + if(isnum(max_body_temp)) + max_body_temp = max_body_temp_ + + if(isnum(cold_damage_per_tick)) + cold_damage_per_tick = cold_damage_per_tick_ + + if(isnum(heat_damage_per_tick)) + heat_damage_per_tick = heat_damage_per_tick_ + + RegisterSignal(target, COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT, PROC_REF(handle_temperature)) + +/datum/element/body_temperature/Detach(datum/target) + UnregisterSignal(target, COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT) + return ..() + +/datum/element/body_temperature/proc/handle_temperature(mob/living/simple_animal/target, datum/gas_mixture/readonly_environment) + SIGNAL_HANDLER // COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT + + if(!readonly_environment) + return + + var/areatemp = target.get_temperature(readonly_environment) + + if(abs(areatemp - target.bodytemperature) > 5 && !HAS_TRAIT(src, TRAIT_NOBREATH)) + var/diff = areatemp - target.bodytemperature + diff = diff / 5 + target.bodytemperature += diff + + if(target.bodytemperature < min_body_temp) + target.adjustHealth(cold_damage_per_tick) + else if(target.bodytemperature > max_body_temp) + target.adjustHealth(heat_damage_per_tick) + +#undef NONCARBON_DEFAULT_MIN_BODY_TEMP +#undef NONCARBON_DEFAULT_MAX_BODY_TEMP +#undef NONCARBON_DEFAULT_COLD_DAMAGE +#undef NONCARBON_DEFAULT_HEAT_DAMAGE diff --git a/code/modules/mob/living/living_life.dm b/code/modules/mob/living/living_life.dm index e5cde5004aad..afba51de5a4a 100644 --- a/code/modules/mob/living/living_life.dm +++ b/code/modules/mob/living/living_life.dm @@ -116,7 +116,8 @@ /mob/living/proc/handle_random_events() return -/mob/living/proc/handle_environment(datum/gas_mixture/environment) +/// Handle temperature/pressure differences between body and environment +/mob/living/proc/handle_environment() return /mob/living/proc/update_pulling() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 9b2a77bc05e5..6ac0309330a4 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -162,6 +162,9 @@ AddComponent(/datum/component/footstep, footstep_type) add_strippable_element() + apply_atmos_requirements() + AddElement(/datum/element/body_temperature, minbodytemp, maxbodytemp, cold_damage_per_tick, heat_damage_per_tick) + /mob/living/simple_animal/Destroy() /// We need to clear the reference to where we're walking to properly GC walk_to(src, 0) @@ -181,8 +184,25 @@ if(T && AIStatus == AI_Z_OFF) SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + remove_atmos_requirements() + RemoveElement(/datum/element/body_temperature) + return ..() +/mob/living/simple_animal/proc/apply_atmos_requirements() + if(unsuitable_atmos_damage == 0) + return + + /* + * String associated list returns a cached list. + * This is like a static list to pass into the element below. + */ + atmos_requirements = string_assoc_list(atmos_requirements) + AddElement(/datum/element/atmos_requirements, atmos_requirements, unsuitable_atmos_damage) + +/mob/living/simple_animal/proc/remove_atmos_requirements() + RemoveElement(/datum/element/atmos_requirements) + /mob/living/simple_animal/handle_atom_del(atom/A) if(A == pcollar) pcollar = null @@ -288,64 +308,8 @@ else custom_emote(EMOTE_AUDIBLE, pick(emote_hear)) - /mob/living/simple_animal/handle_environment(datum/gas_mixture/readonly_environment) - if(!readonly_environment) - return - var/atmos_suitable = 1 - - var/areatemp = get_temperature(readonly_environment) - - if(abs(areatemp - bodytemperature) > 5 && !HAS_TRAIT(src, TRAIT_NOBREATH)) - var/diff = areatemp - bodytemperature - diff = diff / 5 - bodytemperature += diff - - var/tox = readonly_environment.toxins() - var/oxy = readonly_environment.oxygen() - var/n2 = readonly_environment.nitrogen() - var/co2 = readonly_environment.carbon_dioxide() - - if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) - atmos_suitable = 0 - throw_alert("not_enough_oxy", /atom/movable/screen/alert/not_enough_oxy) - else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) - atmos_suitable = 0 - throw_alert("too_much_oxy", /atom/movable/screen/alert/too_much_oxy) - else - clear_alert("not_enough_oxy") - clear_alert("too_much_oxy") - - if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) - atmos_suitable = 0 - throw_alert("not_enough_tox", /atom/movable/screen/alert/not_enough_tox) - else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) - atmos_suitable = 0 - throw_alert("too_much_tox", /atom/movable/screen/alert/too_much_tox) - else - clear_alert("too_much_tox") - clear_alert("not_enough_tox") - - if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) - atmos_suitable = 0 - - if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) - atmos_suitable = 0 - - if(!atmos_suitable) - adjustHealth(unsuitable_atmos_damage) - - handle_temperature_damage() - -/mob/living/simple_animal/proc/handle_temperature_damage() - if(bodytemperature < minbodytemp) - adjustHealth(cold_damage_per_tick) - else if(bodytemperature > maxbodytemp) - adjustHealth(heat_damage_per_tick) + SEND_SIGNAL(src, COMSIG_SIMPLEANIMAL_HANDLE_ENVIRONMENT, readonly_environment) /mob/living/simple_animal/gib() if(icon_gib) diff --git a/paradise.dme b/paradise.dme index cdaabb5dbbab..7cfd4db571e8 100644 --- a/paradise.dme +++ b/paradise.dme @@ -505,6 +505,8 @@ #include "code\datums\diseases\advance\symptoms\weight.dm" #include "code\datums\diseases\advance\symptoms\youth.dm" #include "code\datums\elements\_element.dm" +#include "code\datums\elements\atmos_requirements.dm" +#include "code\datums\elements\body_temperature.dm" #include "code\datums\elements\bombable_turf.dm" #include "code\datums\elements\earhealing.dm" #include "code\datums\elements\rad_insulation.dm"