From 477cc076d8e519b3e514e2eb9eacab12c50defcd Mon Sep 17 00:00:00 2001 From: Cameron Lennox Date: Sun, 26 Oct 2025 22:21:17 -0400 Subject: [PATCH] Radiation additions & Component (#18642) * It begins * Better coding practices * No mapwide radiation glow! * Update radiation_effects.dm * A bit more efficient * Theeese too * moar * caps * lint * Stops self contamiantion * Mregen Fixes this bug with mRegen. Also gives bruteloss the 1% heal chance. * MAR! * TGUI panel * Nuclear png Attribution not required, but being listed for posterity: From uxwing. Icon name is "Radiation Icon SVG Vector" * colorful * Update nuclear.scss * Update damage_procs.dm * Radiation Negative Trait * Update radiation_effects.dm * get rid of this bad documentation Should only EVER be registerwithparent. --- code/__defines/dcs/signals.dm | 11 + code/__defines/life.dm | 3 + code/_global_vars/traits/_traits.dm | 2 + .../components/traits/_trait_template.dm | 36 ++- .../components/traits/radiation_effects.dm | 221 ++++++++++++++++++ code/modules/mob/living/carbon/alien/life.dm | 5 +- code/modules/mob/living/carbon/brain/life.dm | 5 +- code/modules/mob/living/carbon/human/life.dm | 88 +++---- .../carbon/human/species/outsider/shadow.dm | 2 +- .../carbon/human/species/shadekin/shadekin.dm | 2 +- .../living/carbon/human/species/species.dm | 9 +- .../human/species/station/prometheans.dm | 2 + .../species/station/protean/protean_blob.dm | 5 +- .../carbon/human/species/station/station.dm | 8 +- .../human/species/station/traits/negative.dm | 6 + .../human/species/station/traits/neutral.dm | 14 ++ .../human/species/station/traits/positive.dm | 20 ++ code/modules/mob/living/damage_procs.dm | 7 +- code/modules/mob/living/life.dm | 14 +- code/modules/radiation/radiation.dm | 2 + tgui/packages/tgui/assets/bg-radiation.svg | 1 + tgui/packages/tgui/index.tsx | 1 + .../tgui/interfaces/RadiationConfig.tsx | 59 +++++ tgui/packages/tgui/styles/themes/nuclear.scss | 23 ++ .../tgui/styles/themes/spookyconsole.scss | 1 + vorestation.dme | 1 + 26 files changed, 469 insertions(+), 79 deletions(-) create mode 100644 code/datums/components/traits/radiation_effects.dm create mode 100644 tgui/packages/tgui/assets/bg-radiation.svg create mode 100644 tgui/packages/tgui/interfaces/RadiationConfig.tsx create mode 100644 tgui/packages/tgui/styles/themes/nuclear.scss diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index df9b2bc4f5..7c14390b27 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -450,6 +450,11 @@ #define COMSIG_LIVING_CAN_TRACK "mob_cantrack" #define COMPONENT_CANT_TRACK (1<<0) +///from base of /mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1, rad_protection) +#define COMSIG_LIVING_IRRADIATE_EFFECT "living_irradiate_effect" + ///Return this if you want to block the effect. + #define COMPONENT_BLOCK_IRRADIATION (1<<0) + // /mob/living/carbon signals ///from base of mob/living/carbon/soundbang_act(): (list(intensity)) @@ -694,6 +699,12 @@ #define COMSIG_HUMAN_DNA_FINALIZED "human_dna_finished" ///from /proc/domutcheck(): () #define COMSIG_MOB_DNA_MUTATION "mob_dna_mutation" +///from /mob/living/proc/handle_radiation() +#define COMSIG_HANDLE_RADIATION "handle_radiation" + #define COMPONENT_BLOCK_LIVING_RADIATION (1<<0) +///from /mob/living/proc/handle_mutations() +#define COMSIG_HANDLE_MUTATIONS "handle_mutations" + #define COMPONENT_BLOCK_LIVING_MUTATIONS (1<<0) // Organ specific signals diff --git a/code/__defines/life.dm b/code/__defines/life.dm index dec65d9e78..bf582d06b8 100644 --- a/code/__defines/life.dm +++ b/code/__defines/life.dm @@ -20,3 +20,6 @@ #define TECHNOMANCER_INSTABILITY_MIN_GLOW 10 // When above this number, the entity starts glowing, affecting others. #define RADIATION_SPEED_COEFFICIENT 0.1 + +///Maximum amount of radiation an entity can have. +#define RADIATION_CAP 5000 diff --git a/code/_global_vars/traits/_traits.dm b/code/_global_vars/traits/_traits.dm index 7710ae1959..bf3102719d 100644 --- a/code/_global_vars/traits/_traits.dm +++ b/code/_global_vars/traits/_traits.dm @@ -4,11 +4,13 @@ // Please do note that there is absolutely no bearing on what traits are added to what subtype of `/datum`, this is just an easily referenceable list sorted by type. // The only thing that truly matters about traits is the code that is built to handle the traits, and where that code is located. Nothing else. +#define WEAKENED_RADIATION_RESISTANCE "Weakened" #define NORMAL_RADIATION_RESISTANCE "Normal" #define RESISTANT_RADIATION_RESISTANCE "Resistant" #define MAJOR_RESISTANT_RADIATION_RESISTANCE "Major Resistance" #define IMMUNITY_RADIATION_RESISTANCE "Immunity" GLOBAL_LIST_INIT(radiation_levels, list( + WEAKENED_RADIATION_RESISTANCE = list("safe" = 15, "danger_1" = 50, "danger_2" = 150, "danger_3" = 200, "danger_4" = 750), NORMAL_RADIATION_RESISTANCE = list("safe" = 50, "danger_1" = 100, "danger_2" = 300, "danger_3" = 400, "danger_4" = 1500), RESISTANT_RADIATION_RESISTANCE = list("safe" = 70, "danger_1" = 150, "danger_2" = 450, "danger_3" = 600, "danger_4" = 2250), MAJOR_RESISTANT_RADIATION_RESISTANCE = list("safe" = 150, "danger_1" = 300, "danger_2" = 600, "danger_3" = 1000, "danger_4" = 3000), diff --git a/code/datums/components/traits/_trait_template.dm b/code/datums/components/traits/_trait_template.dm index 53e6052a7b..5fb09916ef 100644 --- a/code/datums/components/traits/_trait_template.dm +++ b/code/datums/components/traits/_trait_template.dm @@ -17,6 +17,8 @@ //Reference to our owner (the person that possesses us) //This can be anything that is our owner that we want to reference. Swap out with mob/living/carbon/human/owner as needed. var/mob/living/owner //easy reference + //NOTE: Alternatively, you can do something like "var/mob/living/owner = parent" in procs where needed to avoid having a hard ref to our owner... + //It's just a matter of preference at that point. //dupe_mode = COMPONENT_DUPE_HIGHLANDER //Default mode. See flags.dm @@ -29,10 +31,17 @@ owner = parent add_verb(owner,/mob/living/proc/example_proc) //We can add verbs to our owner. - RegisterSignal(owner, COMSIG_EXAMPLE_SIGNAL, PROC_REF(example_proc)) //To put this easily: Owner is the person we're attached to, COMSIG_EXAMPLE_SIGNAL is the signal we expect them to send out when they want us to use our 'example_proc' + +/datum/component/template/RegisterWithParent() + //To put this easily: Owner is the person we're attached to, COMSIG_EXAMPLE_SIGNAL is the signal we expect them to send out when they want us to use our 'example_proc' + RegisterSignal(parent, COMSIG_EXAMPLE_SIGNAL, PROC_REF(example_proc)) //Register this to a signal that is sent out whenever you want this to be called. For example: We want this trait to happen every life() tick, so we register it to the COMSIG_LIVING_LIFE signal that is sent every time life() is called on a /mob. - RegisterSignal(owner, COMSIG_LIVING_LIFE, PROC_REF(process)) + RegisterSignal(parent, COMSIG_LIVING_LIFE, PROC_REF(process)) + +/datum/component/template/UnregisterFromParent() + //IF we registered a signal, we need to unregister it. This can be a list or done separtely. It's suggested to do it as a list. + UnregisterSignal(parent, list(COMSIG_EXAMPLE_SIGNAL, COMSIG_LIVING_LIFE)) /datum/component/template/process() if (QDELETED(parent)) @@ -40,8 +49,6 @@ energy = min(100, energy+1) //Add one energy per tick, up to 100 /datum/component/template/Destroy(force = FALSE) - UnregisterSignal(owner, COMSIG_LIVING_LIFE) //IF we registered a signal, we need to unregister it. - UnregisterSignal(owner, COMSIG_EXAMPLE_SIGNAL) //MAKE SURE TO UNREGISTER YOUR SIGNALS. SERIOUSLY. OR THE SERVER WILL DIE. owner = null //MAKE SURE TO CLEAR YOUR REFS! . = ..() @@ -54,11 +61,26 @@ /datum/component/template/proc/example_proc() - if (stat == DEAD) + if(stat == DEAD) return - if (energy <= 0) //Check a variable on the component. + if(energy <= 0) //Check a variable on the component. to_chat(owner, span_danger("You currently have no energy!")) - else if (cooldown > world.time) //Check the cooldown variable on the component and compare it. + else if(cooldown > world.time) //Check the cooldown variable on the component and compare it. + var/time_to_wait = (cooldown - world.time) / (1 SECONDS) //Simple cooldown + to_chat(owner, span_warning("You're currently on cooldown! Wait for another [round(time_to_wait,0.1)] seconds!")) + return + else + cooldown = world.time + 5 SECONDS //Set the component on a 5 second cooldown. + to_chat(owner, span_warning("You successfully used the example proc!")) + +//Variant of the example proc but under the presumption we don't have a ref to owner. +/datum/component/template/proc/example_proc2() + var/mob/living/owner = parent + if(owner.stat == DEAD) + return + if(energy <= 0) //Check a variable on the component. + to_chat(owner, span_danger("You currently have no energy!")) + else if(cooldown > world.time) //Check the cooldown variable on the component and compare it. var/time_to_wait = (cooldown - world.time) / (1 SECONDS) //Simple cooldown to_chat(owner, span_warning("You're currently on cooldown! Wait for another [round(time_to_wait,0.1)] seconds!")) return diff --git a/code/datums/components/traits/radiation_effects.dm b/code/datums/components/traits/radiation_effects.dm new file mode 100644 index 0000000000..f4dc440554 --- /dev/null +++ b/code/datums/components/traits/radiation_effects.dm @@ -0,0 +1,221 @@ + +/* Component that handles species effects for mobs/species when they are afflicted with radiation. + * Allows for glowing, healing, contamination, and immunity. + */ +/datum/component/radiation_effects + ///Below this value, no glow occurs. + var/radiation_glow_threshold = 50 + + ///If we spread radiation or not. + var/contamination = FALSE + ///Strength of our contamination, if we contaminate. Each 1 strength is 100% of the rads we're dissipating. + var/contamination_strength = 0.1 + ///What level our radiation has to be above to begin to contaminate our surroundings. + var/contamination_threshold = 600 + + ///If we glow or not. + var/glows = TRUE + + ///What color we glow. + var/radiation_color = "#c3f314" + ///Intensity modifier of our glow + var/intensity_mod = 1 + ///Range modifier of our glow + var/range_mod = 1 + ///How much we divide our radiation by to determine how far our glow is. + var/range_coefficient = 100 + ///How much we divide our radiation by to determine how intense our glow is. + var/intensity_coefficient = 150 + + ///If we are immune to radiation damage or not. + var/radiation_immunity = FALSE + + ///If we heal from radiation or not + var/radiation_healing = FALSE + + ///If we dissipate radiation or keep it. + var/radiation_dissipation = TRUE + +/datum/component/radiation_effects/Initialize(glows, radiation_glow_minor_threshold, contamination, contamination_strength, radiation_color, intensity_mod, range_mod, radiation_immunity, radiation_healing, radiation_dissipation) + + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + if(glows) + src.glows = glows + if(radiation_glow_threshold) + src.radiation_glow_threshold = radiation_glow_threshold + if(contamination) + src.contamination = contamination + if(contamination_strength) + src.contamination_strength = contamination_strength + if(radiation_color) + src.radiation_color = radiation_color + if(intensity_mod) + src.intensity_mod = intensity_mod + if(range_mod) + src.range_mod = range_mod + if(radiation_immunity) + src.radiation_immunity = radiation_immunity + if(radiation_healing) + src.radiation_healing = radiation_healing + if(radiation_dissipation) + src.radiation_dissipation = radiation_dissipation + + add_verb(parent, /mob/living/proc/radiation_control_panel) + +/datum/component/radiation_effects/RegisterWithParent() + RegisterSignal(parent, COMSIG_HANDLE_RADIATION, PROC_REF(process_component)) + RegisterSignal(parent, COMSIG_LIVING_LIFE, PROC_REF(process_glow)) + RegisterSignal(parent, COMSIG_LIVING_IRRADIATE_EFFECT, PROC_REF(handle_irradiate_effect)) + +/datum/component/radiation_effects/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_HANDLE_RADIATION, COMSIG_LIVING_LIFE, COMSIG_LIVING_IRRADIATE_EFFECT)) + +/datum/component/radiation_effects/proc/process_glow() + SIGNAL_HANDLER + var/mob/living/living_guy = parent + if(!glows) + if(living_guy.glow_override) //Toggled glow off while we were still actively glowing. + living_guy.glow_override = FALSE + living_guy.set_light(0) + return + if(living_guy.radiation < radiation_glow_threshold) + living_guy.glow_override = FALSE + living_guy.set_light(0) + return + + if(glows) + var/light_range = CLAMP((living_guy.radiation/range_coefficient) * range_mod, 1, 7) //Min 1, max 7 + var/light_power = CLAMP(living_guy.radiation/intensity_coefficient * intensity_mod, 1, 10) + + living_guy.set_light(l_range = light_range, l_power = light_power, l_color = radiation_color, l_on = TRUE) + living_guy.glow_override = TRUE + +///Handles the radiation removal, immunity, and healing effects. +/datum/component/radiation_effects/proc/process_component() + SIGNAL_HANDLER + var/mob/living/living_guy = parent + if(living_guy.radiation > RADIATION_CAP) + living_guy.radiation = CLAMP(living_guy.radiation,0,RADIATION_CAP) + living_guy.accumulated_rads = CLAMP(living_guy.accumulated_rads,0,RADIATION_CAP) + + if(QDELETED(parent)) + return + + //Radiation calculation, done here since contamination uses it + var/rad_removal_mod = 1 + var/rads = living_guy.radiation * 0.04 + + if(ishuman(living_guy)) + var/mob/living/carbon/human/human_guy = parent + rad_removal_mod = human_guy.species.rad_removal_mod + //End of the calculation. + + if(contamination && living_guy.radiation > contamination_threshold) + SSradiation.radiate(living_guy, rads * contamination_strength * rad_removal_mod) + + if(radiation_immunity || radiation_healing) + //We have to remove radiation here since we're blocking radiation altogether. + var/rads_to_utilize = rads * rad_removal_mod + + //If we heal from radiation, we will dissipate (use up) the amount we heal. + if(radiation_healing) + living_guy.radiation -= rads_to_utilize + living_guy.accumulated_rads -= rads_to_utilize + rads_to_utilize = CLAMP(rads_to_utilize, 1, 10) //Only heal up to 10 rads. + living_guy.adjust_nutrition(rads_to_utilize) + living_guy.adjustBruteLoss(-rads_to_utilize) + living_guy.adjustFireLoss(-rads_to_utilize) + living_guy.adjustOxyLoss(-rads_to_utilize) + living_guy.adjustToxLoss(-rads_to_utilize) + living_guy.updatehealth() + + else if(radiation_dissipation) + living_guy.radiation -= rads_to_utilize + living_guy.accumulated_rads -= rads_to_utilize + + return COMPONENT_BLOCK_LIVING_RADIATION + +/datum/component/radiation_effects/proc/handle_irradiate_effect(var/mob/living/living_guy, var/effect, var/effecttype, var/blocked, var/check_protection, var/rad_protection) + SIGNAL_HANDLER + ///If we're not contaminating, don't worry about this. Proceed like normal. + if(!contamination || (contamination && living_guy.radiation < contamination_threshold)) + //to_chat(world, "Radiation like normal. Current rads = [living_guy.radiation]. Amount of rads being added = [effect].") + return + + var/rad_removal_mod = 1 + if(ishuman(living_guy)) + var/mob/living/carbon/human/human_guy = parent + rad_removal_mod = human_guy.species.rad_removal_mod + + var/radiation_offput = ((living_guy.radiation * 0.04) * contamination_strength * rad_removal_mod) + var/radiation_to_apply = (effect - radiation_offput) + if(radiation_to_apply > 0) + // to_chat(world, "Radiation blocker. Current rads = [living_guy.radiation]. Original = [effect] RTA = [radiation_to_apply] After protection = [radiation_to_apply * rad_protection]. Amount of rads we're offputting = [radiation_offput]") + + //This stops MOST of the radiation we're offputting from hitting us. + //If we linger in one place for a prolonged period, the area around us will become irradiated and give us a small bit of radiation back. (only got ~1 rad per tick when we were offputting 60 rads for example) + //However, we'll lose our rads faster than we accumulate. + living_guy.radiation += max((radiation_to_apply * rad_protection), 0) + return COMPONENT_BLOCK_IRRADIATION +/datum/component/radiation_effects/promethean + radiation_immunity = TRUE + +/datum/component/radiation_effects/shadekin + radiation_immunity = TRUE + glows = FALSE + +///Heals from radiation. Does not glow. +/datum/component/radiation_effects/diona + glows = FALSE + radiation_healing = TRUE + +///TGUI below here +//TGUI Weaver Panel +/datum/component/radiation_effects/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RadiationConfig", "Radiation Config") + ui.open() + + +/mob/living/proc/radiation_control_panel() + set name = "Radiation Control Panel" + set desc = "Allows you to adjust the settings of various radioactive settings!" + set category = "Abilities.Radiation" + + var/datum/component/radiation_effects/rad = get_radiation_component() + if(!rad) + to_chat(src, span_warning("You don't have the radiation component! This is a bug! Please report this to a maintainer.")) + return FALSE + + rad.tgui_interact(src) + +/datum/component/radiation_effects/tgui_data(mob/user) + var/data = list( + "glowing" = glows, + "radiation_color" = radiation_color, + ) + + return data + +/datum/component/radiation_effects/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("toggle_color") + var/set_new_color = tgui_color_picker(ui.user, "Select a color you wish your radioactive glow to be!", "Color Selector", radiation_color) + if(!set_new_color) + return FALSE + radiation_color = set_new_color + return TRUE + if("toggle_glow") + glows = !glows + to_chat(parent, span_info("You are [glows ? "now" : "no longer"] glowing.")) + return FALSE + +/mob/living/proc/get_radiation_component() + var/datum/component/radiation_effects/rad = GetComponent(/datum/component/radiation_effects) + if(rad) + return rad diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm index 66040e509c..368643b4b4 100644 --- a/code/modules/mob/living/carbon/alien/life.dm +++ b/code/modules/mob/living/carbon/alien/life.dm @@ -17,7 +17,10 @@ //Status updates, death etc. update_icons() -/mob/living/carbon/alien/handle_mutations_and_radiation() +/mob/living/carbon/alien/handle_radiation() + . = ..() + if(.) + return // Currently both Dionaea and larvae like to eat radiation, so I'm defining the // rad absorbtion here. This will need to be changed if other baby aliens are added. diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm index 35e6b74fa0..09594845f7 100644 --- a/code/modules/mob/living/carbon/brain/life.dm +++ b/code/modules/mob/living/carbon/brain/life.dm @@ -1,7 +1,10 @@ /mob/living/carbon/brain/handle_breathing() return -/mob/living/carbon/brain/handle_mutations_and_radiation() +/mob/living/carbon/brain/handle_radiation() + . = ..() + if(.) + return if (radiation) if (radiation > 100) radiation = 100 diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 98778fe017..11fc681073 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -206,6 +206,37 @@ to_chat(src, span_danger("Your legs won't respond properly, you fall down!")) Weaken(10) +/mob/living/carbon/human/handle_mutations() + . = ..() + if(.) + return + if(inStasisNow()) + return + + if(getFireLoss()) + if((COLD_RESISTANCE in mutations) || (prob(1))) + heal_organ_damage(0,1) + if(getBruteLoss()) //Fireloss gets this RNG change so may as well give bruteloss it as well. + if(prob(1)) + heal_organ_damage(1,0) + + if((mRegen in mutations)) + var/heal = rand(0.2,1.3) + if(prob(50)) + for(var/obj/item/organ/external/O in organs) //HAS to be organs and NOT bad_external_organs as a fully healed limb w/ internal damage will NOT be in bad_external_organs + for(var/datum/wound/W in O.wounds) + if(W.bleeding()) + W.damage = max(W.damage - heal, 0) + if(W.damage <= 0) + O.wounds -= W + if(W.internal) + W.damage = max(W.damage - heal, 0) + if(W.damage <= 0) + O.wounds -= W + else + heal_organ_damage(heal,heal) + + // RADIATION! Everyone's favorite thing in the world! So let's get some numbers down off the bat. // 50 rads = 1Bq. This means 1 rad = 0.02Bq. // However, unless I am a smoothbrained dumbo, absorbed rads are in Gy. Not Bq. @@ -227,65 +258,20 @@ // Additionally, RADIATION_SPEED_COEFFICIENT = 0.1 -/mob/living/carbon/human/handle_mutations_and_radiation() //Radiation rework! Now with 'accumulated_rads' +/mob/living/carbon/human/handle_radiation() //Radiation rework! Now with 'accumulated_rads' + . = ..() + if(.) + return if(inStasisNow()) return - if(getFireLoss()) - if((COLD_RESISTANCE in mutations) || (prob(1))) - heal_organ_damage(0,1) - - if(stat != DEAD) - if((mRegen in mutations)) - var/heal = rand(0.2,1.3) - if(prob(50)) - for(var/obj/item/organ/external/O in bad_external_organs) - for(var/datum/wound/W in O.wounds) - if(W.bleeding()) - W.damage = max(W.damage - heal, 0) - if(W.damage <= 0) - O.wounds -= W - if(W.internal) - W.damage = max(W.damage - heal, 0) - if(W.damage <= 0) - O.wounds -= W - else - heal_organ_damage(heal,heal) - - radiation = CLAMP(radiation,0,5000) //Max of 100Gy. If you reach that...You're going to wish you were dead. You probably will be dead. - accumulated_rads = CLAMP(accumulated_rads,0,5000) //Max of 100Gy as well. You should never get higher than this. You will be dead before you can reach this. + radiation = CLAMP(radiation,0,RADIATION_CAP) //Max of 100Gy. If you reach that...You're going to wish you were dead. You probably will be dead. + accumulated_rads = CLAMP(accumulated_rads,0,RADIATION_CAP) //Max of 100Gy as well. You should never get higher than this. You will be dead before you can reach this. var/obj/item/organ/internal/I = null //Used for further down below when an organ is picked. if(!radiation) - if(species.appearance_flags & RADIATION_GLOWS) - glow_override = FALSE - set_light(0) if(accumulated_rads) accumulated_rads -= RADIATION_SPEED_COEFFICIENT //Accumulated rads slowly dissipate very slowly. Get to medical to get it treated! else if(((life_tick % 5 == 0) && radiation) || (radiation > 600)) //Radiation is a slow, insidious killer. Unless you get a massive dose, then the onset is sudden! - if(species.appearance_flags & RADIATION_GLOWS) - glow_override = TRUE - set_light(max(1,min(5,radiation/15)), max(1,min(10,radiation/25)), species.get_flesh_colour(src)) - // END DOGSHIT SNOWFLAKE - - var/obj/item/organ/internal/diona/nutrients/rad_organ = locate() in internal_organs - if(rad_organ && !rad_organ.is_broken()) - var/rads = radiation/25 - radiation -= (rads * species.rad_removal_mod) - adjust_nutrition(rads * species.rad_removal_mod) - adjustBruteLoss(-(rads * species.rad_removal_mod)) - adjustFireLoss(-(rads * species.rad_removal_mod)) - adjustOxyLoss(-(rads * species.rad_removal_mod)) - adjustToxLoss(-(rads * species.rad_removal_mod)) - updatehealth() - return - - var/obj/item/organ/internal/brain/slime/core = locate() in internal_organs - if(core) - return - - var/obj/item/organ/internal/brain/shadekin/s_brain = locate() in internal_organs - if(s_brain) - return if(reagents.has_reagent(REAGENT_ID_PRUSSIANBLUE)) //Prussian Blue temporarily stops radiation effects. return diff --git a/code/modules/mob/living/carbon/human/species/outsider/shadow.dm b/code/modules/mob/living/carbon/human/species/outsider/shadow.dm index 9ef803df5e..a17aa88ef5 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/shadow.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/shadow.dm @@ -26,7 +26,7 @@ assisted_langs = list() - species_component = /datum/component/burninlight/shadow // Until a parent component like xenochimera have is needed, only handles burning in light. + species_component = list(/datum/component/burninlight/shadow) // Until a parent component like xenochimera have is needed, only handles burning in light. /datum/species/shadow/handle_death(var/mob/living/carbon/human/H) spawn(1) diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm index de58ad4afc..856dc487da 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm @@ -100,7 +100,7 @@ BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) ) - species_component = /datum/component/shadekin + species_component = list(/datum/component/shadekin, /datum/component/radiation_effects/shadekin) component_requires_late_recalc = TRUE /datum/species/shadekin/handle_death(var/mob/living/carbon/human/H) diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 7ee7f90e51..ee7a9a09cd 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -246,7 +246,7 @@ var/list/env_traits = list() var/pixel_offset_x = 0 // Used for offsetting 64x64 and up icons. var/pixel_offset_y = 0 // Used for offsetting 64x64 and up icons. - var/rad_levels = NORMAL_RADIATION_RESISTANCE //For handle_mutations_and_radiation + var/rad_levels = NORMAL_RADIATION_RESISTANCE //For handle_radiation var/rad_removal_mod = 1 var/ambulant_blood = FALSE // Force changeling blood effects @@ -359,7 +359,7 @@ var/list/food_preference = list() //RS edit var/food_preference_bonus = 0 - var/datum/component/species_component = null // The component that this species uses. Example: Xenochimera use /datum/component/xenochimera + var/list/species_component = list() // The component that this species uses. Example: Xenochimera use /datum/component/xenochimera var/component_requires_late_recalc = FALSE // If TRUE, the component will do special recalculation stuff at the end of update_icons_body() // For Lleill and Hanner @@ -804,8 +804,9 @@ ..() /datum/species/proc/apply_components(var/mob/living/carbon/human/H) - if(species_component) - H.LoadComponent(species_component) + if(LAZYLEN(species_component)) + for(var/component in species_component) + H.LoadComponent(component) /datum/species/proc/produceCopy(var/list/traits, var/mob/living/carbon/human/H, var/custom_base, var/reset_dna = TRUE) // Traitgenes reset_dna flag required, or genes get reset on resleeve ASSERT(src) diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans.dm b/code/modules/mob/living/carbon/human/species/station/prometheans.dm index 7579d4c59f..e33befa18f 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans.dm @@ -35,6 +35,8 @@ var/datum/species/shapeshifter/promethean/prometheans secondary_langs = list(LANGUAGE_PROMETHEAN, LANGUAGE_SOL_COMMON) // For some reason, having this as their species language does not allow it to be chosen. assisted_langs = list(LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX) // Prometheans are weird, let's just assume they can use basically any language. + species_component = list(/datum/component/radiation_effects/promethean) + blood_name = "gelatinous ooze" blood_reagents = REAGENT_ID_SLIMEJELLY diff --git a/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm index dab9974ab2..c4c2bfc92f 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean/protean_blob.dm @@ -657,13 +657,14 @@ return 1 return 0 -/mob/living/simple_mob/protean_blob/handle_mutations_and_radiation() +/mob/living/simple_mob/protean_blob/handle_radiation() + ..() if(!humanform) to_chat(src, span_giant(span_boldwarning("You are currently a blob without a humanform and should be deleted shortly Please report what you were doing when this error occurred to the admins."))) stack_trace("URGENT, SERVER-CRASHING ISSUE: A protean blob does not have a humanform! src = [src] ckey = [ckey]! The blob has been deleted.") qdel(src) return - humanform.handle_mutations_and_radiation() + humanform.handle_radiation() /mob/living/simple_mob/protean_blob/update_icon() ..() diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index cb7c66e102..21dc48536e 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -490,7 +490,7 @@ reagent_tag = IS_ZADDAT - species_component = /datum/component/burninlight // Until a parent component like xenochimera have is needed, only handles burning in light. + species_component = list(/datum/component/burninlight) // Until a parent component like xenochimera have is needed, only handles burning in light. heat_discomfort_strings = list( "Your joints itch.", @@ -564,6 +564,8 @@ min_age = 18 max_age = 300 + species_component = list(/datum/component/radiation_effects/diona) + economic_modifier = 10 blurb = "Commonly referred to (erroneously) as 'plant people', the Dionaea are a strange space-dwelling collective \ @@ -1548,7 +1550,7 @@ tail = "tail" //Spider tail. icobase_tail = 1 - species_component = /datum/component/weaver + species_component = list(/datum/component/weaver) inherent_verbs = list( /mob/living/carbon/human/proc/tie_hair) @@ -1741,7 +1743,7 @@ reagent_tag = IS_CHIMERA - species_component = /datum/component/xenochimera + species_component = list(/datum/component/xenochimera) /datum/species/xenochimera/handle_environment_special(var/mob/living/carbon/human/H) //Cold/pressure effects when not regenerating diff --git a/code/modules/mob/living/carbon/human/species/station/traits/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits/negative.dm index 5deefde9d2..88c458cee1 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits/negative.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits/negative.dm @@ -677,3 +677,9 @@ else if(istype(ex_organ, /obj/item/organ/external/chest)) ex_organ.encased = FALSE + +/datum/trait/negative/rad_weakness + name = "Radiation Weakness" + desc = "You are approximately 50% more susceptible to radiation, and it dissipates slower from your body." + cost = -2 + var_changes = list("radiation_mod" = 1.5, "rad_removal_mod" = 0.5, "rad_levels" = WEAKENED_RADIATION_RESISTANCE) diff --git a/code/modules/mob/living/carbon/human/species/station/traits/neutral.dm b/code/modules/mob/living/carbon/human/species/station/traits/neutral.dm index c40a950bd7..20c4f8d7c2 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits/neutral.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits/neutral.dm @@ -1795,3 +1795,17 @@ /datum/trait/neutral/strongimmunesystem/apply(datum/species/S, mob/living/carbon/human/H, trait_prefs) ..() ADD_TRAIT(H, STRONG_IMMUNITY_TRAIT, ROUNDSTART_TRAIT) + +/datum/trait/neutral/glowing_radiation + name = "Radioactive Glow" + desc = "You emit a glow when exposed to radiation! This does not prevent you from being harmed by radiation." + cost = 0 + has_preferences = list("glow_color" = list(TRAIT_PREF_TYPE_COLOR, "Glow color", TRAIT_VAREDIT_TARGET_MOB, "#c3f314")) + added_component_path = /datum/component/radiation_effects + excludes = list(/datum/trait/positive/radioactive_heal) + +/datum/trait/neutral/glowing_radiation/apply(var/datum/species/S,var/mob/living/carbon/human/H, var/list/trait_prefs) + ..() + var/datum/component/radiation_effects/G = H.GetComponent(added_component_path) + if(trait_prefs) + G.radiation_color = trait_prefs["glow_color"] diff --git a/code/modules/mob/living/carbon/human/species/station/traits/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits/positive.dm index dd0cc6cad7..fb08941b7e 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits/positive.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits/positive.dm @@ -542,3 +542,23 @@ can_take = ORGANICS var_changes = list("virus_immune" = TRUE) + +/datum/trait/positive/radioactive_heal + name = "Radioactive Heal" + desc = "You heal when exposed to radiation instead of becoming ill. You can also choose to emit a glow when irradiated." + cost = 6 + is_genetrait = TRUE + hidden = FALSE + has_preferences = list("glow_color" = list(TRAIT_PREF_TYPE_COLOR, "Glow color", TRAIT_VAREDIT_TARGET_MOB, "#c3f314",), + "glow_enabled" = list(TRAIT_PREF_TYPE_BOOLEAN, "Glow enabled on spawn", TRAIT_VAREDIT_TARGET_MOB, FALSE)) + + added_component_path = /datum/component/radiation_effects + excludes = list(/datum/trait/neutral/glowing_radiation, /datum/trait/positive/rad_resistance, /datum/trait/positive/rad_resistance_extreme, /datum/trait/positive/rad_immune, /datum/trait/negative/rad_weakness) + +/datum/trait/positive/radioactive_heal/apply(var/datum/species/S,var/mob/living/carbon/human/H, var/list/trait_prefs) + ..() + var/datum/component/radiation_effects/G = H.GetComponent(added_component_path) + if(trait_prefs) + G.radiation_color = trait_prefs["glow_color"] + G.glows = trait_prefs["glow_enabled"] + G.radiation_healing = TRUE diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 2a9047fd94..11f6d065ad 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -159,13 +159,10 @@ if(AGONY) halloss += max((effect * blocked), 0) // Useful for objects that cause "subdual" damage. PAIN! if(IRRADIATE) - /* - var/rad_protection = check_protection ? getarmor(null, "rad")/100 : 0 - radiation += max((1-rad_protection)*effect/(blocked+1),0)//Rads auto check armor - */ var/rad_protection = getarmor(null, "rad") rad_protection = (100-rad_protection)/100 - radiation += max((effect * rad_protection), 0) + if(!(SEND_SIGNAL(src, COMSIG_LIVING_IRRADIATE_EFFECT, effect, effecttype, blocked, check_protection, rad_protection) & COMPONENT_BLOCK_IRRADIATION)) + radiation += max((effect * rad_protection), 0) if(STUTTER) if(status_flags & CANSTUN) // stun is usually associated with stutter stuttering = max(stuttering,(effect * blocked)) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index f028948cd1..b61258a88f 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -25,7 +25,8 @@ handle_breathing() //Mutations and radiation - handle_mutations_and_radiation() + handle_mutations() + handle_radiation() @@ -89,8 +90,15 @@ /mob/living/proc/handle_breathing() return -/mob/living/proc/handle_mutations_and_radiation() - return +/mob/living/proc/handle_mutations() + SHOULD_CALL_PARENT(TRUE) + if(SEND_SIGNAL(src, COMSIG_HANDLE_MUTATIONS) & COMPONENT_BLOCK_LIVING_MUTATIONS) + return COMPONENT_BLOCK_LIVING_MUTATIONS + +/mob/living/proc/handle_radiation() + SHOULD_CALL_PARENT(TRUE) + if(SEND_SIGNAL(src, COMSIG_HANDLE_RADIATION) & COMPONENT_BLOCK_LIVING_RADIATION) + return COMPONENT_BLOCK_LIVING_RADIATION /mob/living/proc/handle_chemicals_in_body() return diff --git a/code/modules/radiation/radiation.dm b/code/modules/radiation/radiation.dm index 32db1b7a69..b4d0a2d5e2 100644 --- a/code/modules/radiation/radiation.dm +++ b/code/modules/radiation/radiation.dm @@ -22,6 +22,8 @@ qdel(src) // Decayed to nothing else rad_power = new_power + if(rad_power > 1e15) // Arbitrary cap to prevent going to infinity + rad_power = 1e15 if(!flat) range = min(round(sqrt(rad_power / CONFIG_GET(number/radiation_lower_limit))), 31) // R = rad_power / dist**2 - Solve for dist return diff --git a/tgui/packages/tgui/assets/bg-radiation.svg b/tgui/packages/tgui/assets/bg-radiation.svg new file mode 100644 index 0000000000..bfda81d043 --- /dev/null +++ b/tgui/packages/tgui/assets/bg-radiation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tgui/packages/tgui/index.tsx b/tgui/packages/tgui/index.tsx index 918897b63b..39910c3534 100644 --- a/tgui/packages/tgui/index.tsx +++ b/tgui/packages/tgui/index.tsx @@ -9,6 +9,7 @@ import './styles/main.scss'; import './styles/themes/abductor.scss'; import './styles/themes/cardtable.scss'; import './styles/themes/spookyconsole.scss'; +import './styles/themes/nuclear.scss'; import './styles/themes/hackerman.scss'; import './styles/themes/crtsoul.scss'; import './styles/themes/malfunction.scss'; diff --git a/tgui/packages/tgui/interfaces/RadiationConfig.tsx b/tgui/packages/tgui/interfaces/RadiationConfig.tsx new file mode 100644 index 0000000000..3fb4579cd0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RadiationConfig.tsx @@ -0,0 +1,59 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { + Button, + ColorBox, + LabeledList, + Section, + Stack, +} from 'tgui-core/components'; + +type Data = { + radiation_color: string | null; + glowing: number; +}; + +export const RadiationConfig = (props) => { + const { act, data } = useBackend(); + + const { + radiation_color, + glowing, + } = data; + return ( + + + + +
+ + + + + + + + + + + + + act('toggle_glow')} + /> + + +
+
+
+
+
+ ); + }; diff --git a/tgui/packages/tgui/styles/themes/nuclear.scss b/tgui/packages/tgui/styles/themes/nuclear.scss new file mode 100644 index 0000000000..4820b796f4 --- /dev/null +++ b/tgui/packages/tgui/styles/themes/nuclear.scss @@ -0,0 +1,23 @@ +.theme-nuclear:root { + // Base + --color-base: hsl(71, 100%, 13%); + --color-primary: hsl(73, 95%, 30%); + --color-good: hsl(73, 95%, 46%); + --color-average: hsl(64, 99%, 34%); + --color-bad: hsl(74, 100%, 25%); + --base-gradient-spread: 12; + --secondary-lightness-adjustment: 9; + + // Components + --button-background-selected: var(--color-good); + --button-background-caution: hsl(240, 80%, 35%); + --button-background-danger: hsl(282, 61%, 30%); + --dimmer-background-opacity: 0.45; + --notice-box-background: hsl(64, 90%, 23%); + --notice-box-color: var(--color-fixed-white); + --progress-bar-background: hsla(0, 97%, 7%, 0.5); + + .Layout__content { + background-image: url('../../assets/bg-radiation.svg'); + } +} diff --git a/tgui/packages/tgui/styles/themes/spookyconsole.scss b/tgui/packages/tgui/styles/themes/spookyconsole.scss index ec2797414b..f0cb6c388f 100644 --- a/tgui/packages/tgui/styles/themes/spookyconsole.scss +++ b/tgui/packages/tgui/styles/themes/spookyconsole.scss @@ -3,6 +3,7 @@ --color-base: hsl(0, 100%, 12.5%); --color-primary: hsl(345, 95%, 30%); --color-good: hsl(0, 82%, 52%); + --color-average: hsl(0, 77%, 44%); --color-bad: hsl(340, 91%, 32%); --base-gradient-spread: 12; --secondary-lightness-adjustment: 9; diff --git a/vorestation.dme b/vorestation.dme index 514f4dcec4..d3caec512f 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -666,6 +666,7 @@ #include "code\datums\components\traits\gargoyle.dm" #include "code\datums\components\traits\nutrition_size_change.dm" #include "code\datums\components\traits\photosynth.dm" +#include "code\datums\components\traits\radiation_effects.dm" #include "code\datums\components\traits\unlucky.dm" #include "code\datums\components\traits\waddle.dm" #include "code\datums\components\traits\weaver.dm"