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"