Radiation Refactor (#19270)

* Part 1

* WIP

* The rest of these

* More stuff

* Whoops, did that wrong

* typo

* gweeen

* This all works

* SHOWER

* Rads

* awa

* rad

* Update life.dm

* edits

* Makes lvl 3 rads give you a warning.

You should already know by this point, but this makes it EXTRA clear you're getting fucked

* Update vorestation.dme

* aaa

* propagate

* gwah

* more fixes

* AAA

* Update radiation.dm

* Update radiation.dm

* mobs rads

* rads

* fix this

* Update _reagents.dm

* these

* Get rid of these

* rad

* Update config.txt

* fixed

* Update radiation_effects.dm
This commit is contained in:
Cameron Lennox
2026-03-22 12:29:09 -04:00
committed by GitHub
parent d91baf9c9f
commit cbc4151bfb
113 changed files with 2044 additions and 551 deletions
+4
View File
@@ -25,8 +25,12 @@ GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur")))
#define isradio(A) istype(A, /obj/item/radio)
#define isclothing(A) istype(A, /obj/item/clothing)
#define isairlock(A) istype(A, /obj/machinery/door/airlock)
#define ismachinery(A) istype(A, /obj/machinery)
#define isorgan(A) istype(A, /obj/item/organ/external)
#define isstorage(A) istype(A, /obj/item/storage)
+1
View File
@@ -11,6 +11,7 @@
//Investigate logging defines
#define INVESTIGATE_CARGO "cargo"
#define INVESTIGATE_RESEARCH "research"
#define INVESTIGATE_RADIATION "radiation"
// Logging types for log_message()
#define LOG_ATTACK (1 << 0)
+8
View File
@@ -0,0 +1,8 @@
/// Flags for specifically what kind of items to get in get_equipped_items
#define INCLUDE_POCKETS (1<<0)
#define INCLUDE_ACCESSORIES (1<<1)
#define INCLUDE_HELD (1<<2)
/// Include prosthetic item limbs (which are not flavoured as being equipped items)
#define INCLUDE_PROSTHETICS (1<<3)
/// Include items that are not "real" items, such as hand items
#define INCLUDE_ABSTRACT (1<<4)
+66
View File
@@ -0,0 +1,66 @@
/*
These defines are the balancing points of various parts of the radiation system.
Changes here can have widespread effects: make sure you test well.
Ask Mothblocks if they're around
*/
/// How much stored radiation to check for hair loss
#define RAD_MOB_HAIRLOSS (1 MINUTES)
/// Chance of you hair starting to fall out every second when over threshold
#define RAD_MOB_HAIRLOSS_PROB 7.5
/// How much stored radiation to check for mutation
#define RAD_MOB_MUTATE (2 MINUTES)
/// Chance of randomly mutating every second when over threshold
#define RAD_MOB_MUTATE_PROB 0.5
/// The time since irradiated before checking for vomitting
#define RAD_MOB_VOMIT (2 MINUTES)
/// Chance per second of vomitting
#define RAD_MOB_VOMIT_PROB 0.5
/// How much stored radiation to check for stunning
#define RAD_MOB_KNOCKDOWN (2 MINUTES)
/// Chance of knockdown per second when over threshold
#define RAD_MOB_KNOCKDOWN_PROB 0.5
/// Amount of knockdown when it occurs
#define RAD_MOB_KNOCKDOWN_AMOUNT 3
#define RAD_NO_INSULATION 1.0 // For things that shouldn't become irradiated for whatever reason
#define RAD_VERY_LIGHT_INSULATION 0.9 // What girders have
#define RAD_LIGHT_INSULATION 0.8
#define RAD_MEDIUM_INSULATION 0.7 // What common walls have
#define RAD_HEAVY_INSULATION 0.6 // What reinforced walls have
#define RAD_EXTREME_INSULATION 0.5 // What rad collectors have
#define RAD_FULL_INSULATION 0 // Completely stops radiation from coming through
/// The default chance something can be irradiated
#define DEFAULT_RADIATION_CHANCE 10
/// The default chance for uranium structures to irradiate
#define URANIUM_IRRADIATION_CHANCE DEFAULT_RADIATION_CHANCE
/// The minimum exposure time before uranium structures can irradiate
#define URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME (3 SECONDS)
/// The minimum exposure time before the radioactive nebula can irradiate
#define NEBULA_RADIATION_MINIMUM_EXPOSURE_TIME (6 SECONDS)
/// Return values of [proc/get_perceived_radiation_danger]
// If you change these, update /datum/looping_sound/geiger as well.
#define PERCEIVED_RADIATION_DANGER_LOW 1
#define PERCEIVED_RADIATION_DANGER_MEDIUM 2
#define PERCEIVED_RADIATION_DANGER_HIGH 3
#define PERCEIVED_RADIATION_DANGER_EXTREME 4
/// The time before geiger counters reset back to normal without any radiation pulses
#define TIME_WITHOUT_RADIATION_BEFORE_RESET (5 SECONDS)
// Radiation exposure params
// For the radioactive nebula outside
/// Base chance the nebula has of applying irradiation
#define RADIATION_EXPOSURE_NEBULA_BASE_CHANCE 20
/// The chance we add to the base chance every time we fail to irradiate
#define RADIATION_EXPOSURE_NEBULA_CHANCE_INCREMENT 10
/// Time it takes for the next irradiation check
#define RADIATION_EXPOSURE_NEBULA_CHECK_INTERVAL 5 SECONDS
+7
View File
@@ -198,3 +198,10 @@
/// The timer key used to know how long subsystem initialization takes
#define SS_INIT_TIMER_KEY "ss_init"
// Subsystem delta times or tickrates, in seconds. I.e, how many seconds in between each process() call for objects being processed by that subsystem.
// Only use these defines if you want to access some other objects processing seconds_per_tick, otherwise use the seconds_per_tick that is sent as a parameter to process()
// #define SSFLUIDS_DT (SSplumbing.wait/10)
#define SSMACHINES_DT (SSmachines.wait/10)
#define SSMOBS_DT (SSmobs.wait/10)
#define SSOBJ_DT (SSobj.wait/10)
+17
View File
@@ -19,3 +19,20 @@
#define TOOL_KNIFE "knife"
#define TOOL_BLOODFILTER "bloodfilter"
#define TOOL_ROLLINGPIN "rollingpin"
/**
* A helper for checking if an item interaction should be skipped.
* This is only used explicitly because some interactions may not want to ever be skipped.
*/
#define SHOULD_SKIP_INTERACTION(target, item, user) (HAS_TRAIT(target, TRAIT_COMBAT_MODE_SKIP_INTERACTION) && (user.a_intent == I_HURT))
/// Return when an item interaction is successful.
/// This cancels the rest of the chain entirely and indicates success.
#define ITEM_INTERACT_SUCCESS (1<<0) // Same as TRUE, as most tool (legacy) tool acts return TRUE on success
/// Return to prevent the rest of the attack chain from being executed / preventing the item user from thwacking the target.
/// Similar to [ITEM_INTERACT_SUCCESS], but does not necessarily indicate success.
#define ITEM_INTERACT_BLOCKING (1<<1)
/// Only for people who get confused by the naming scheme
#define ITEM_INTERACT_FAILURE ITEM_INTERACT_BLOCKING
/// Return to skip the rest of the interaction chain, going straight to attack.
#define ITEM_INTERACT_SKIP_TO_ATTACK (1<<2)
+32
View File
@@ -59,3 +59,35 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_SLOBBER "slobber"
// Owner will be considered a tiny mob for some interactions, such as airlocks not opening unless they have a client, or being vacuumed up by the vacpack
#define TRAIT_AMBIENT_PEST_MOB "ambient_pest_mob"
/// Marks that this object is irradiated
#define TRAIT_IRRADIATED "iraddiated"
/// Whether or not this item will allow the radiation SS to go through standard
/// radiation processing as if this wasn't already irradiated.
/// Basically, without this, COMSIG_IN_RANGE_OF_IRRADIATION won't fire once the object is irradiated.
#define TRAIT_BYPASS_EARLY_IRRADIATED_CHECK "radiation_bypass_early_irradiated_check"
/// Immune to being irradiated
#define TRAIT_RADIMMUNE "rad_immunity"
/// This clothing protects the user from radiation.
/// This should not be used on clothing_traits, but should be applied to the clothing itself.
#define TRAIT_RADIATION_PROTECTED_CLOTHING "radiation_protected_clothing"
/// Trait applied by MODsuits.
#define MOD_TRAIT "mod"
/// The mob has the stasis effect.
/// Does nothing on its own, applied via status effect.
#define TRAIT_STASIS "in_stasis"
/// Harmful radiation effects, the toxin damage and the burns, will not occur while this trait is active
#define TRAIT_HALT_RADIATION_EFFECTS "halt_radiation_effects"
/**
*
* This trait is used in some interactions very high in the interaction chain to allow
* certain atoms to be skipped by said interactions if the user is in combat mode.
*
* Its primarily use case is for stuff like storage and tables, to allow things like emags to be bagged
* (because in some contexts you might want to be emagging a bag, and in others you might want to be storing it.)
*
* This is only checked by certain items explicitly so you can't just add the trait and expect it to work.
* (This may be changed later but I chose to do it this way to avoid messing up interactions which require combat mode)
* While /tg/ calls it combat mode, we just specifiy it as having your a_intent set to I_HURT
*/
#define TRAIT_COMBAT_MODE_SKIP_INTERACTION "combat_mode_skip_interaction"
+4 -2
View File
@@ -33,11 +33,13 @@
//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
///Returns a list of turf in a square
#define RANGE_TURFS(RADIUS, CENTER) \
RECT_TURFS(RADIUS, RADIUS, CENTER)
#define RECT_TURFS(H_RADIUS, V_RADIUS, CENTER) \
block( \
locate(max(CENTER.x-(H_RADIUS),1), max(CENTER.y-(V_RADIUS),1), CENTER.z), \
locate(min(CENTER.x+(H_RADIUS),world.maxx), min(CENTER.y+(V_RADIUS),world.maxy), CENTER.z) \
(CENTER).x - (H_RADIUS), (CENTER).y - (V_RADIUS), (CENTER).z, \
(CENTER).x + (H_RADIUS), (CENTER).y + (V_RADIUS), (CENTER).z \
)
// Wet turfs have different slipping intensities
+7
View File
@@ -21,6 +21,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
/atom = list(
"TRAIT_ALT_CLICK_BLOCKER" = TRAIT_ALT_CLICK_BLOCKER,
),
/atom/movable = list(
"TRAIT_IRRADIATED" = TRAIT_IRRADIATED,
),
/mob = list(
"TRAIT_THINKING_IN_CHARACTER" = TRAIT_THINKING_IN_CHARACTER,
"TRAIT_NOFIRE" = TRAIT_NOFIRE,
@@ -29,6 +32,10 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_TESLA_SHOCKIMMUNE" = TRAIT_TESLA_SHOCKIMMUNE,
"TRAIT_MADNESS_IMMUNE" = TRAIT_MADNESS_IMMUNE,
"TRAIT_AMBIENT_PEST_MOB" = TRAIT_AMBIENT_PEST_MOB,
"TRAIT_BYPASS_EARLY_IRRADIATED_CHECK" = TRAIT_BYPASS_EARLY_IRRADIATED_CHECK,
"TRAIT_RADIMMUNE" = TRAIT_RADIMMUNE,
"TRAIT_STASIS" = TRAIT_STASIS,
"TRAIT_HALT_RADIATION_EFFECTS" = TRAIT_HALT_RADIATION_EFFECTS,
),
/obj = list(
"TRAIT_CLIMBABLE" = TRAIT_CLIMBABLE,
+45
View File
@@ -0,0 +1,45 @@
/**
* Get a list of turfs in a line from `starting_atom` to `ending_atom`.
*
* Uses the ultra-fast [Bresenham Line-Drawing Algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm).
*/
/proc/get_line(atom/starting_atom, atom/ending_atom)
var/current_x_step = starting_atom.x//start at x and y, then add 1 or -1 to these to get every turf from starting_atom to ending_atom
var/current_y_step = starting_atom.y
var/starting_z = starting_atom.z
var/list/line = list(get_turf(starting_atom))//get_turf(atom) is faster than locate(x, y, z)
var/x_distance = ending_atom.x - current_x_step //x distance
var/y_distance = ending_atom.y - current_y_step
var/abs_x_distance = abs(x_distance)//Absolute value of x distance
var/abs_y_distance = abs(y_distance)
var/x_distance_sign = SIGN(x_distance) //Sign of x distance (+ or -)
var/y_distance_sign = SIGN(y_distance)
var/x = abs_x_distance >> 1 //Counters for steps taken, setting to distance/2
var/y = abs_y_distance >> 1 //Bit-shifting makes me l33t. It also makes get_line() unnecessarily fast.
if(abs_x_distance >= abs_y_distance) //x distance is greater than y
for(var/distance_counter in 0 to (abs_x_distance - 1))//It'll take abs_x_distance steps to get there
y += abs_y_distance
if(y >= abs_x_distance) //Every abs_y_distance steps, step once in y direction
y -= abs_x_distance
current_y_step += y_distance_sign
current_x_step += x_distance_sign //Step on in x direction
line += locate(current_x_step, current_y_step, starting_z)//Add the turf to the list
else
for(var/distance_counter in 0 to (abs_y_distance - 1))
x += abs_x_distance
if(x >= abs_y_distance)
x -= abs_y_distance
current_x_step += x_distance_sign
current_y_step += y_distance_sign
line += locate(current_x_step, current_y_step, starting_z)
return line
+85
View File
@@ -0,0 +1,85 @@
/// Whether or not it's possible for this atom to be irradiated
#define CAN_IRRADIATE(atom) (ismob(##atom))
/// Calculates the max chance for a radiation_pulse via a radioactive reagent
#define CALCULATE_RAD_MAX_CHANCE(rad_power) (20 + (15 * (rad_power - 1)))
/// Sends out a pulse of radiation, eminating from the source.
/// Radiation is performed by collecting all radiatables within the max range (0 means source only, 1 means adjacent, etc),
/// then makes their way towards them. A number, starting at 1, is multiplied
/// by the insulation amounts of whatever is in the way (for example, walls lowering it down).
/// If this number hits equal or below the threshold, then the target can no longer be irradiated.
/// If the number is above the threshold, then the chance is the chance that the target will be irradiated.
/// As a consumer, this means that max_range going up usually means you want to lower the threshold too,
/// as well as the other way around.
/// If max_range is high, but threshold is too high, then it usually won't reach the source at the max range in time.
/// If max_range is low, but threshold is too low, then it basically guarantees everyone nearby, even if there's walls
/// and such in the way, can be irradiated.
/// You can also pass in a minimum exposure time. If this is set, then this radiation pulse
/// will not irradiate the source unless they have been around *any* radioactive source for that
/// period of time.
/// The chance to get irradiated diminishes over range, and from objects that block radiation.
/// Assuming there is nothing in the way, the chance will determine what the chance is to get irradiated from half of max_range.
/// Example: If chance is equal to 30%, and max_range is equal to 8,
/// then the chance for a thing to get irradiated is 30% if they are 4 turfs away from the pulse source.
/// Also, strength is how much radiation the target will get if they fail their RNG check / linger for too long.
/proc/radiation_pulse(
atom/source,
max_range,
threshold,
chance = DEFAULT_RADIATION_CHANCE,
minimum_exposure_time = 0,
strength = 100
)
if(!SSradiation.can_fire)
return
var/datum/radiation_pulse_information/pulse_information = new
pulse_information.source_ref = WEAKREF(source)
pulse_information.max_range = max_range
pulse_information.threshold = threshold
pulse_information.chance = chance
pulse_information.minimum_exposure_time = minimum_exposure_time
pulse_information.turfs_to_process = RANGE_TURFS(max_range, source)
pulse_information.strength = strength
SSradiation.processing += pulse_information
return TRUE
/datum/radiation_pulse_information
var/datum/weakref/source_ref
var/max_range
var/threshold
var/chance
var/minimum_exposure_time
var/list/turfs_to_process
var/strength
#define MEDIUM_RADIATION_THRESHOLD_RANGE 0.5
#define EXTREME_RADIATION_CHANCE 30
/// Gets the perceived "danger" of radiation pulse, given the threshold to the target.
/// Returns a RADIATION_DANGER_* define, see [code/__DEFINES/radiation.dm]
/proc/get_perceived_radiation_danger(datum/radiation_pulse_information/pulse_information, insulation_to_target)
if (insulation_to_target > pulse_information.threshold)
// We could get irradiated! The only thing stopping us now is chance, so scale based on that.
if (pulse_information.chance >= EXTREME_RADIATION_CHANCE)
return PERCEIVED_RADIATION_DANGER_EXTREME
else
return PERCEIVED_RADIATION_DANGER_HIGH
else
// We're out of the threshold from being irradiated, but by how much?
if (insulation_to_target / pulse_information.threshold <= MEDIUM_RADIATION_THRESHOLD_RANGE)
return PERCEIVED_RADIATION_DANGER_MEDIUM
else
return PERCEIVED_RADIATION_DANGER_LOW
/// A common proc used to send COMSIG_ATOM_PROPAGATE_RAD_PULSE to adjacent atoms
/// Only used for uranium (false/tram)walls to spread their radiation pulses
/atom/proc/propagate_radiation_pulse()
for(var/atom/atom in orange(1,src))
SEND_SIGNAL(atom, COMSIG_ATOM_PROPAGATE_RAD_PULSE, src)
#undef MEDIUM_RADIATION_THRESHOLD_RANGE
#undef EXTREME_RADIATION_CHANCE
+8
View File
@@ -0,0 +1,8 @@
///Returns the distance between two atoms
/proc/get_dist_euclidean(atom/first_location, atom/second_location)
var/dx = first_location.x - second_location.x
var/dy = first_location.y - second_location.y
var/dist = sqrt(dx ** 2 + dy ** 2)
return dist
-7
View File
@@ -7,13 +7,6 @@
//Checks if all high bits in req_mask are set in bitfield
#define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0)
//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
#define RANGE_TURFS(RADIUS, CENTER) \
block( \
locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
)
//Returns the middle-most value
/proc/dd_range(var/low, var/high, var/num)
return max(low,min(high,num))
+5
View File
@@ -307,6 +307,11 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
var/mob/living/L = usr
return L.resist()
/atom/movable/screen/alert/irradiated
name = "Irradiated"
desc = "You're irradiated! Heal your radiation quick, and stand under a shower to wash some radiation off from yourself!"
// use_user_hud_icon = TRUE
icon_state = "irradiated"
//ALIENS
@@ -603,40 +603,6 @@
/datum/config_entry/str_list/language_prefixes
default = list(",", "#", "-")
// 0:1 subtraction:division for computing effective radiation on a turf
/// 0 / RAD_RESIST_CALC_DIV = Each turf absorbs some fraction of the working radiation level
/// 1 / RAD_RESIST_CALC_SUB = Each turf absorbs a fixed amount of radiation
/datum/config_entry/flag/radiation_resistance_calc_mode
default = RAD_RESIST_CALC_SUB
/datum/config_entry/flag/radiation_resistance_calc_mode/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
var/val_as_num = text2num(str_val)
if(val_as_num in list(RAD_RESIST_CALC_DIV, RAD_RESIST_CALC_SUB))
config_entry_value = val_as_num
return TRUE
return FALSE
///How much radiation is reduced by each tick
/datum/config_entry/number/radiation_decay_rate
default = 1
integer = FALSE
/datum/config_entry/number/radiation_resistance_multiplier
default = 8.5 //VOREstation edit
integer = FALSE
/datum/config_entry/number/radiation_material_resistance_divisor
default = 1
min_val = 0.1
integer = FALSE
///If the radiation level for a turf would be below this, ignore it.
/datum/config_entry/number/radiation_lower_limit
default = 0.35
integer = FALSE
/// If true, submaps loaded automatically can be rotated.
/datum/config_entry/flag/random_submap_orientation
+208
View File
@@ -0,0 +1,208 @@
#define RADIATION_IMMEDIATE_TOX_DAMAGE 10
#define RADIATION_TOX_DAMAGE_PER_INTERVAL 2
#define RADIATION_TOX_INTERVAL (25 SECONDS)
#define RADIATION_BURN_SPLOTCH_DAMAGE 11
#define RADIATION_BURN_INTERVAL_MIN (30 SECONDS)
#define RADIATION_BURN_INTERVAL_MAX (60 SECONDS)
// Showers process on SSmachines
#define RADIATION_CLEAN_IMMUNITY_TIME (SSMACHINES_DT + (1 SECONDS))
/// This atom is irradiated, and will glow green.
/// Humans will take toxin damage until all their toxin damage is cleared.
/datum/component/irradiated
dupe_mode = COMPONENT_DUPE_UNIQUE
var/beginning_of_irradiation
var/burn_splotch_timer_id
COOLDOWN_DECLARE(clean_cooldown)
COOLDOWN_DECLARE(last_tox_damage)
/datum/component/irradiated/Initialize()
if (!CAN_IRRADIATE(parent))
return COMPONENT_INCOMPATIBLE
// This isn't incompatible, it's just wrong
if (HAS_TRAIT(parent, TRAIT_RADIMMUNE))
qdel(src)
return
ADD_TRAIT(parent, TRAIT_IRRADIATED, REF(src))
create_glow()
beginning_of_irradiation = world.time
if (ishuman(parent))
var/mob/living/carbon/human/human_parent = parent
human_parent.apply_damage(RADIATION_IMMEDIATE_TOX_DAMAGE, TOX)
START_PROCESSING(SSobj, src)
COOLDOWN_START(src, last_tox_damage, RADIATION_TOX_INTERVAL)
start_burn_splotch_timer()
human_parent.throw_alert("irradiated", /atom/movable/screen/alert/irradiated)
/datum/component/irradiated/RegisterWithParent()
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean))
RegisterSignal(parent, COMSIG_GEIGER_COUNTER_SCAN, PROC_REF(on_geiger_counter_scan))
RegisterSignal(parent, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_healthscan))
/datum/component/irradiated/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_GEIGER_COUNTER_SCAN,
COMSIG_LIVING_HEALTHSCAN,
))
/datum/component/irradiated/Destroy(force)
var/atom/movable/parent_movable = parent
if (istype(parent_movable))
parent_movable.remove_filter("rad_glow")
var/mob/living/carbon/human/human_parent = parent
if (istype(human_parent))
human_parent.clear_alert("irradiated")
REMOVE_TRAIT(parent, TRAIT_IRRADIATED, REF(src))
deltimer(burn_splotch_timer_id)
STOP_PROCESSING(SSobj, src)
return ..()
/datum/component/irradiated/process(seconds_per_tick)
if (!ishuman(parent))
return PROCESS_KILL
if (HAS_TRAIT(parent, TRAIT_RADIMMUNE))
qdel(src)
return PROCESS_KILL
var/mob/living/carbon/human/human_parent = parent
if (human_parent.radiation == 0)
qdel(src)
return PROCESS_KILL
if (should_halt_effects(parent))
return
// if (human_parent.stat != DEAD)
// human_parent.handle_radiation()
// human_parent.dna?.species?.handle_radiation(human_parent, world.time - beginning_of_irradiation, seconds_per_tick)
// process_tox_damage(human_parent, seconds_per_tick)
/datum/component/irradiated/proc/should_halt_effects(mob/living/carbon/human/target)
if (HAS_TRAIT(target, TRAIT_STASIS))
return TRUE
if (HAS_TRAIT(target, TRAIT_HALT_RADIATION_EFFECTS))
return TRUE
if (!COOLDOWN_FINISHED(src, clean_cooldown))
return TRUE
return FALSE
/datum/component/irradiated/proc/process_tox_damage(mob/living/carbon/human/target, seconds_per_tick)
if (!COOLDOWN_FINISHED(src, last_tox_damage))
return
target.apply_damage(RADIATION_TOX_DAMAGE_PER_INTERVAL, TOX)
COOLDOWN_START(src, last_tox_damage, RADIATION_TOX_INTERVAL)
/datum/component/irradiated/proc/start_burn_splotch_timer()
addtimer(CALLBACK(src, PROC_REF(give_burn_splotches)), rand(RADIATION_BURN_INTERVAL_MIN, RADIATION_BURN_INTERVAL_MAX), TIMER_STOPPABLE)
/datum/component/irradiated/proc/give_burn_splotches()
// This shouldn't be possible, but just in case.
if (QDELETED(src))
return
start_burn_splotch_timer()
var/mob/living/carbon/human/human_parent = parent
if (should_halt_effects(parent))
return
var/obj/item/organ/external/affected_limb = pick(human_parent.organs)
human_parent.visible_message(
span_boldwarning("[human_parent]'s [affected_limb.name] bubbles unnaturally, then bursts into blisters!"),
span_boldwarning("Your [affected_limb.name] bubbles unnaturally, then bursts into blisters!"),
)
if(human_parent.is_blind())
to_chat(human_parent, span_boldwarning("Your [affected_limb.name] feels like it's bubbling, then burns like hell!"))
human_parent.apply_damage(RADIATION_BURN_SPLOTCH_DAMAGE, BURN, affected_limb, blocked = FALSE, used_weapon = "Radiation Burns")
playsound(
human_parent,
get_sfx("sizzle"),
50,
vary = TRUE,
)
/datum/component/irradiated/proc/create_glow()
var/atom/movable/parent_movable = parent
if (!istype(parent_movable))
return
parent_movable.add_filter("rad_glow", 2, list("type" = "outline", "color" = "#39ff1430", "size" = 2))
addtimer(CALLBACK(src, PROC_REF(start_glow_loop), parent_movable), rand(0.1 SECONDS, 1.9 SECONDS)) // Things should look uneven
/datum/component/irradiated/proc/start_glow_loop(atom/movable/parent_movable)
var/filter = parent_movable.get_filter("rad_glow")
if (!filter)
return
animate(filter, alpha = 110, time = 1.5 SECONDS, loop = -1)
animate(alpha = 40, time = 2.5 SECONDS)
/datum/component/irradiated/proc/on_clean(datum/source, clean_types)
SIGNAL_HANDLER
if (!(clean_types & CLEAN_TYPE_RADIATION))
return NONE
if (isitem(parent))
qdel(src)
return COMPONENT_CLEANED|COMPONENT_CLEANED_GAIN_XP
COOLDOWN_START(src, clean_cooldown, RADIATION_CLEAN_IMMUNITY_TIME)
/datum/component/irradiated/proc/on_geiger_counter_scan(datum/source, mob/user, obj/item/geiger/geiger_counter)
SIGNAL_HANDLER
if (isliving(source))
var/mob/living/living_source = source
to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Subject is irradiated. Contamination traces back to roughly [DisplayTimeText(world.time - beginning_of_irradiation, 5)] ago. Current toxin levels: [living_source.getToxLoss()]."))
else
// In case the green wasn't obvious enough...
to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Target is irradiated."))
return COMSIG_GEIGER_COUNTER_SCAN_SUCCESSFUL
/datum/component/irradiated/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode, tochat)
SIGNAL_HANDLER
render_list += span_notice("Subject is irradiated. Supply antiradiation or antitoxin, such as [/datum/reagent/prussian_blue::name].")
// render_list += "<span class='alert ml-1'>"
//render_list += conditional_tooltip("Subject is irradiated.", "Supply antiradiation or antitoxin, such as [/datum/reagent/prussian_blue::name].", tochat)
// render_list += "</span><br>"
#undef RADIATION_BURN_SPLOTCH_DAMAGE
#undef RADIATION_BURN_INTERVAL_MIN
#undef RADIATION_BURN_INTERVAL_MAX
#undef RADIATION_CLEAN_IMMUNITY_TIME
#undef RADIATION_IMMEDIATE_TOX_DAMAGE
#undef RADIATION_TOX_INTERVAL
#undef RADIATION_TOX_DAMAGE_PER_INTERVAL
+161 -123
View File
@@ -1,148 +1,186 @@
SUBSYSTEM_DEF(radiation)
name = "Radiation"
wait = 2 SECONDS
flags = SS_NO_INIT
flags = SS_BACKGROUND | SS_NO_INIT
var/list/sources = list() // all radiation source datums
var/list/sources_assoc = list() // Sources indexed by turf for de-duplication.
var/list/resistance_cache = list() // Cache of turf's radiation resistance.
wait = 0.5 SECONDS
var/tmp/list/current_sources = list()
var/tmp/list/current_res_cache = list()
var/tmp/list/listeners = list()
/// A list of radiation sources (/datum/radiation_pulse_information) that have yet to process.
/// Do not interact with this directly, use `radiation_pulse` instead.
var/list/datum/radiation_pulse_information/processing = list()
/datum/controller/subsystem/radiation/fire(resumed = FALSE)
if (!resumed)
current_sources = sources.Copy()
current_res_cache = resistance_cache.Copy()
listeners = GLOB.living_mob_list.Copy()
/datum/controller/subsystem/radiation/fire(resumed)
while (processing.len)
var/datum/radiation_pulse_information/pulse_information = processing[1]
while(length(current_sources))
var/datum/radiation_source/S = current_sources[length(current_sources)]
current_sources.len--
var/datum/weakref/source_ref = pulse_information.source_ref
var/atom/source = source_ref.resolve()
if (isnull(source))
processing.Cut(1, 2)
continue
pulse(source, pulse_information)
if(QDELETED(S))
sources -= S
else if(S.decay)
S.update_rad_power(S.rad_power - CONFIG_GET(number/radiation_decay_rate))
if (MC_TICK_CHECK)
return
while(length(current_res_cache))
var/turf/T = current_res_cache[length(current_res_cache)]
current_res_cache.len--
if(QDELETED(T))
resistance_cache -= T
else if((length(T.contents) + 1) != resistance_cache[T])
resistance_cache -= T // If its stale REMOVE it! It will get added if its needed.
if (MC_TICK_CHECK)
return
if(!length(sources))
listeners.Cut()
while(length(listeners))
var/atom/A = listeners[length(listeners)]
listeners.len--
if(!QDELETED(A))
var/turf/T = get_turf(A)
var/rads = get_rads_at_turf(T)
if(rads)
A.rad_act(rads)
if (MC_TICK_CHECK)
return
processing.Cut(1, 2)
/datum/controller/subsystem/radiation/stat_entry(msg)
msg = "S:[length(sources)], RC:[length(resistance_cache)]"
msg = "Pulses:[processing.len]"
return ..()
// Ray trace from all active radiation sources to T and return the strongest effect.
/datum/controller/subsystem/radiation/proc/get_rads_at_turf(var/turf/T)
. = 0
if(!istype(T))
return
/datum/controller/subsystem/radiation/proc/pulse(atom/source, datum/radiation_pulse_information/pulse_information)
var/list/cached_rad_insulations = list()
var/list/cached_turfs_to_process = pulse_information.turfs_to_process
var/turfs_iterated = 0
var/pulse_strength = pulse_information.strength
for (var/turf/turf_to_irradiate as anything in cached_turfs_to_process)
turfs_iterated += 1
for(var/datum/radiation_source/source as anything in sources)
if(source.rad_power < .)
continue // Already being affected by a stronger source
for(var/obj/machinery/power/rad_collector in turf_to_irradiate)
SEND_SIGNAL(rad_collector, COMSIG_IN_RANGE_OF_IRRADIATION, pulse_information, 1) //We just do it here and skip all the math to make it faster. Sure, we could have something blocking the rad collectors, but this is faster and has better CPU gains in exchange for negligible gameplay impact.
continue
if(source.source_turf.z != T.z)
continue // Radiation is not multi-z
for(var/obj/item/geiger/geiger_counter in turf_to_irradiate)
var/current_insulation = 1
for(var/turf/turf_in_between in get_line(source, geiger_counter) - get_turf(source))
var/insulation = cached_rad_insulations[turf_in_between]
if(isnull(insulation))
insulation = turf_in_between.rad_insulation
for (var/atom/on_turf as anything in turf_in_between.contents)
insulation *= on_turf.rad_insulation
cached_rad_insulations[turf_in_between] = insulation
if(source.respect_maint)
var/area/A = T.loc
if(A.flag_check(RAD_SHIELDED))
continue // In shielded area
current_insulation *= insulation
var/dist = get_dist(source.source_turf, T)
if(dist > source.range)
continue // Too far to possibly affect
if(current_insulation <= pulse_information.threshold)
continue
if(source.flat)
. += source.rad_power
continue // No need to ray trace for flat field
SEND_SIGNAL(geiger_counter, COMSIG_IN_RANGE_OF_IRRADIATION, pulse_information, current_insulation)
// Okay, now ray trace to find resistence!
var/turf/origin = source.source_turf
var/working = source.rad_power
while(origin != T)
origin = get_step_towards(origin, T) //Raytracing
if(!resistance_cache[origin]) //Only get the resistance if we don't already know it.
origin.calc_rad_resistance()
if(origin.cached_rad_resistance)
if(CONFIG_GET(flag/radiation_resistance_calc_mode) == RAD_RESIST_CALC_DIV)
working = round((working / (origin.cached_rad_resistance * CONFIG_GET(number/radiation_resistance_multiplier))), 0.01)
else if(CONFIG_GET(flag/radiation_resistance_calc_mode) == RAD_RESIST_CALC_SUB)
working = round((working - (origin.cached_rad_resistance * CONFIG_GET(number/radiation_resistance_multiplier))), 0.01)
for(var/mob/living/target in turf_to_irradiate)
if(!can_irradiate_basic(target))
continue
if(working <= CONFIG_GET(number/radiation_lower_limit)) // Too far from this source
working = 0 // May as well be 0
var/current_insulation = 1
for (var/turf/turf_in_between in get_line(source, target) - get_turf(source))
var/insulation = cached_rad_insulations[turf_in_between]
if (isnull(insulation))
insulation = turf_in_between.rad_insulation
for (var/atom/on_turf as anything in turf_in_between.contents)
insulation *= on_turf.rad_insulation
cached_rad_insulations[turf_in_between] = insulation
current_insulation *= insulation
if (current_insulation <= pulse_information.threshold)
break
SEND_SIGNAL(target, COMSIG_IN_RANGE_OF_IRRADIATION, pulse_information, current_insulation)
// Check a second time, because of TRAIT_BYPASS_EARLY_IRRADIATED_CHECK
if (HAS_TRAIT(target, TRAIT_IRRADIATED))
continue
if (current_insulation <= pulse_information.threshold)
continue
/// Perceived chance of target getting irradiated.
var/perceived_chance
/// Intensity variable which will describe the radiation pulse.
/// It is used by perceived intensity, which diminishes over range. The chance of the target getting irradiated is determined by perceived_intensity.
/// Intensity is calculated so that the chance of getting irradiated at half of the max range is the same as the chance parameter.
var/intensity
/// Diminishes over range. Used by perceived chance, which is the actual chance to get irradiated.
var/perceived_intensity
if(pulse_information.chance < 100) // Prevents log(0) runtime if chance is 100%
intensity = -log(1 - pulse_information.chance / 100) * (1 + pulse_information.max_range / 2) ** 2
perceived_intensity = intensity * INVERSE((1 + get_dist_euclidean(source, target)) ** 2) // Diminishes over range.
perceived_intensity *= (current_insulation - pulse_information.threshold) * INVERSE(1 - pulse_information.threshold) // Perceived intensity decreases as objects that absorb radiation block its trajectory.
perceived_chance = 100 * (1 - NUM_E ** -perceived_intensity)
pulse_strength = pulse_strength * (1 - NUM_E ** -perceived_intensity)
else
perceived_chance = 100
var/irradiation_result = SEND_SIGNAL(target, COMSIG_IN_THRESHOLD_OF_IRRADIATION, pulse_information)
if (irradiation_result & CANCEL_IRRADIATION)
continue
if (pulse_information.minimum_exposure_time && !(irradiation_result & SKIP_MINIMUM_EXPOSURE_TIME_CHECK))
target.AddComponent(/datum/component/radiation_countdown, pulse_information.minimum_exposure_time)
continue
if (!prob(perceived_chance))
continue
if (irradiate_after_basic_checks(target, pulse_strength))
target.investigate_log("was irradiated by [source].", INVESTIGATE_RADIATION)
if(MC_TICK_CHECK)
break
cached_turfs_to_process.Cut(1, turfs_iterated + 1)
/// Will attempt to irradiate the given target, limited through IC means, such as radiation protected clothing.
/datum/controller/subsystem/radiation/proc/irradiate(atom/target, strength)
if (!can_irradiate_basic(target))
return FALSE
irradiate_after_basic_checks(target, strength)
return TRUE
/datum/controller/subsystem/radiation/proc/irradiate_after_basic_checks(mob/living/target, strength)
PRIVATE_PROC(TRUE)
if(!ishuman(target))
if(ismob(target))
target.radiation += strength
return TRUE
return FALSE
/// 0 = full protection, 1 = no protection.
var/rad_vulnerability = 1 - wearing_rad_protected_clothing(target)
if(rad_vulnerability <= 0)
return FALSE
target.radiation += round(strength * rad_vulnerability, 0.1)
// target.AddComponent(/datum/component/irradiated)
return TRUE
/// Returns whether or not the target can be irradiated by any means.
/// Does not check for clothing.
/datum/controller/subsystem/radiation/proc/can_irradiate_basic(atom/target)
if (!CAN_IRRADIATE(target))
return FALSE
if (HAS_TRAIT(target, TRAIT_IRRADIATED) && !HAS_TRAIT(target, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK))
return FALSE
if (HAS_TRAIT(target, TRAIT_RADIMMUNE))
return FALSE
return TRUE
/// Retruns a value from 1 (full protection) to 0 (no protection)
/// If we have 4 limbs and 3 are protected, we would expect to have 0.75 returned.
/datum/controller/subsystem/radiation/proc/wearing_rad_protected_clothing(mob/living/carbon/human/human)
///Check how many limbs we have.
var/limb_count = 0
///Check how many of our limbs are protected.
var/protected_limbs = 0
for(var/obj/item/organ/external/limb as anything in human.organs)
limb_count++
for(var/obj/item/clothing as anything in human.get_clothing_on_part(limb))
if(HAS_TRAIT(clothing, TRAIT_RADIATION_PROTECTED_CLOTHING)) //If our clothing
protected_limbs++
break
// Accumulate radiation from all sources in range, not just the biggest.
// Shouldn't really ever have practical uses, but standing in a room literally made from uranium is more dangerous than standing next to a single uranium vase
. += working / (dist ** 2)
var/rad_resistance = clothing.armor["rad"]
if(prob(rad_resistance))
protected_limbs++
break
if(. <= CONFIG_GET(number/radiation_lower_limit))
. = 0
// Add a radiation source instance to the repository. It will override any existing source on the same turf.
/datum/controller/subsystem/radiation/proc/add_source(var/datum/radiation_source/S)
if(!isturf(S.source_turf))
return
var/datum/radiation_source/existing = sources_assoc[S.source_turf]
if(existing)
qdel(existing)
sources += S
sources_assoc[S.source_turf] = S
// Creates a temporary radiation source that will decay
/datum/controller/subsystem/radiation/proc/radiate(source, power) //Sends out a radiation pulse, taking walls into account
if(!(source && power)) //Sanity checking
return
var/datum/radiation_source/S = new()
S.source_turf = get_turf(source)
S.update_rad_power(power)
add_source(S)
// Sets the radiation in a range to a constant value.
/datum/controller/subsystem/radiation/proc/flat_radiate(source, power, range, var/respect_maint = TRUE) //VOREStation edit; Respect shielded areas by default please.
if(!(source && power && range))
return
var/datum/radiation_source/S = new()
S.flat = TRUE
S.range = range
S.respect_maint = respect_maint
S.source_turf = get_turf(source)
S.update_rad_power(power)
add_source(S)
// Irradiates a full Z-level. Hacky way of doing it, but not too expensive.
/datum/controller/subsystem/radiation/proc/z_radiate(var/atom/source, power, var/respect_maint = TRUE) //VOREStation edit; Respect shielded areas by default please.
if(!(power && source))
return
var/turf/epicentre = locate(round(world.maxx / 2), round(world.maxy / 2), source.z)
flat_radiate(epicentre, power, world.maxx, respect_maint)
return (protected_limbs/limb_count)
+92
View File
@@ -0,0 +1,92 @@
/// Atoms with this component will play sounds depending on nearby radiation
/datum/component/geiger_sound
var/datum/looping_sound/geiger/sound
var/last_parent = null
/datum/component/geiger_sound/Initialize(...)
if (!isatom(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/geiger_sound/Destroy(force)
QDEL_NULL(sound)
if (!isnull(last_parent))
UnregisterSignal(last_parent, COMSIG_IN_RANGE_OF_IRRADIATION)
last_parent = null
return ..()
/datum/component/geiger_sound/RegisterWithParent()
sound = new(list(parent), TRUE)
RegisterSignal(parent, COMSIG_IN_RANGE_OF_IRRADIATION, PROC_REF(on_pre_potential_irradiation))
ADD_TRAIT(parent, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, REF(src))
if (isitem(parent))
var/atom/atom_parent = parent
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
register_to_loc(atom_parent.loc)
/datum/component/geiger_sound/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_MOVABLE_MOVED,
COMSIG_IN_RANGE_OF_IRRADIATION,
))
REMOVE_TRAIT(parent, TRAIT_BYPASS_EARLY_IRRADIATED_CHECK, REF(src))
/datum/component/geiger_sound/proc/on_pre_potential_irradiation(datum/source, datum/radiation_pulse_information/pulse_information, insulation_to_target)
SIGNAL_HANDLER
sound.last_insulation_to_target = insulation_to_target
sound.last_radiation_pulse = pulse_information
sound.start(source)
addtimer(CALLBACK(sound, TYPE_PROC_REF(/datum/looping_sound,stop)), TIME_WITHOUT_RADIATION_BEFORE_RESET, TIMER_UNIQUE | TIMER_OVERRIDE)
/datum/component/geiger_sound/proc/on_moved(atom/source)
SIGNAL_HANDLER
register_to_loc(source.loc)
/datum/component/geiger_sound/proc/register_to_loc(new_loc)
if (last_parent == new_loc)
return
if (!isnull(last_parent))
UnregisterSignal(last_parent, COMSIG_IN_RANGE_OF_IRRADIATION)
last_parent = new_loc
if (!isnull(new_loc))
RegisterSignal(new_loc, COMSIG_IN_RANGE_OF_IRRADIATION, PROC_REF(on_pre_potential_irradiation))
/datum/looping_sound/geiger
mid_sounds = list(
list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1),
list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1),
list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1),
list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1)
)
mid_length = 2
volume = 25
var/datum/radiation_pulse_information/last_radiation_pulse
var/last_insulation_to_target
/datum/looping_sound/geiger/Destroy()
last_radiation_pulse = null
return ..()
/datum/looping_sound/geiger/get_sound()
if (isnull(last_radiation_pulse))
return null
return ..(mid_sounds[get_perceived_radiation_danger(last_radiation_pulse, last_insulation_to_target)])
/datum/looping_sound/geiger/stop(null_parent = FALSE)
. = ..()
last_radiation_pulse = null
@@ -0,0 +1,55 @@
// Should be more than any minimum exposure time coming in
#define TIME_UNTIL_DELETION (10 SECONDS)
/// Begins the countdown before a target can be irradiated.
/// Added by the radiation subsystem when a pulse information has a minimum exposure time.
/// Will clear itself out after a while.
/datum/component/radiation_countdown
/// The time this component was added
var/time_added
/// The shortest minimum time before being irradiated.
/// If the source has an attempted irradiation again outside this timeframe, it will go through.
var/minimum_exposure_time
/datum/component/radiation_countdown/Initialize(minimum_exposure_time)
if (!CAN_IRRADIATE(parent))
return COMPONENT_INCOMPATIBLE
src.minimum_exposure_time = minimum_exposure_time
time_added = world.time
// to_chat(parent, span_userdanger("The air around you feels warm...perhaps you should go somewhere else.")) //Silent.
start_deletion_timer()
/datum/component/radiation_countdown/proc/start_deletion_timer()
addtimer(CALLBACK(src, PROC_REF(remove_self)), TIME_UNTIL_DELETION, TIMER_UNIQUE | TIMER_OVERRIDE)
/datum/component/radiation_countdown/proc/remove_self()
// if (!HAS_TRAIT(parent, TRAIT_IRRADIATED))
// to_chat(parent, span_notice("The air here feels safer."))
qdel(src)
/datum/component/radiation_countdown/RegisterWithParent()
RegisterSignal(parent, COMSIG_IN_THRESHOLD_OF_IRRADIATION, PROC_REF(on_pre_potential_irradiation_within_range))
/datum/component/radiation_countdown/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_IN_THRESHOLD_OF_IRRADIATION)
/datum/component/radiation_countdown/proc/on_pre_potential_irradiation_within_range(datum/source, datum/radiation_pulse_information/pulse_information)
SIGNAL_HANDLER
minimum_exposure_time = min(minimum_exposure_time, pulse_information.minimum_exposure_time)
start_deletion_timer()
// Played with fire, now you might be getting irradiated.
if (world.time - time_added >= minimum_exposure_time)
return SKIP_MINIMUM_EXPOSURE_TIME_CHECK
return CANCEL_IRRADIATION
#undef TIME_UNTIL_DELETION
@@ -0,0 +1,79 @@
/// For directly applying to carbons to irradiate them, without pulses
/datum/component/radioactive_exposure
dupe_mode = COMPONENT_DUPE_ALLOWED
/// Base irradiation chance
var/irradiation_chance_base
/// Chance we have of applying irradiation
var/irradiation_chance
/// The amount the base chance is increased after every failed irradiation check
var/irradiation_chance_increment
/// Time till we attempt the next irradiation check
var/irradiation_interval
/// The source of irradiation, for logging
var/source
/// Area's where the component isnt removed if we cross to them
var/list/radioactive_areas
/datum/component/radioactive_exposure/Initialize(
minimum_exposure_time,
irradiation_chance_base,
irradiation_chance_increment,
irradiation_interval,
source,
radioactive_areas
)
if(!iscarbon(parent))
return COMPONENT_INCOMPATIBLE
src.irradiation_chance_base = irradiation_chance_base
src.irradiation_chance = irradiation_chance_base
src.irradiation_chance_increment = irradiation_chance_increment
src.irradiation_interval = irradiation_interval
src.source = source
src.radioactive_areas = radioactive_areas
// We use generally long times, so it's probably easier and more interpretable to just use a timer instead of processing the component
addtimer(CALLBACK(src, PROC_REF(attempt_irradiate)), minimum_exposure_time)
RegisterSignal(parent, COMSIG_MOVABLE_EXITED_AREA, PROC_REF(on_exited))
var/mob/living/living_parent = parent
living_parent.throw_alert("radioactive_area", /atom/movable/screen/alert/radioactive_area)
/// Try and irradiate them. If we chance fail, we come back harder
/datum/component/radioactive_exposure/proc/attempt_irradiate()
if(!SSradiation.wearing_rad_protected_clothing(parent) && SSradiation.can_irradiate_basic(parent))
if(prob(irradiation_chance))
SSradiation.irradiate(parent)
var/atom/atom = parent
atom.investigate_log("was irradiated by [source].", INVESTIGATE_RADIATION)
else
irradiation_chance += irradiation_chance_increment
else // we're immune, either through species, clothing, already being irradiated, etcetera
// we slowly decrease the prob chance untill we hit the base probability again
irradiation_chance = max(irradiation_chance - irradiation_chance_increment, irradiation_chance_base)
// Even if they are immune, or got irradiated plan a new check in-case they lose their protection or irradiation
addtimer(CALLBACK(src, PROC_REF(attempt_irradiate)), irradiation_interval)
/datum/component/radioactive_exposure/proc/on_exited(atom/movable/also_parent, area/old_area, direction)
SIGNAL_HANDLER
if(istype(get_area(parent), radioactive_areas)) //we left to another area that is also radioactive, so dont do anything
return
qdel(src)
/datum/component/radioactive_exposure/Destroy(force)
var/mob/living/carbon/human/human_parent = parent
human_parent.clear_alert("radioactive_area")
return ..()
/atom/movable/screen/alert/radioactive_area
name = "Radioactive Area"
desc = "This place is no good! We need to get some protection or get out fast!"
// use_user_hud_icon = TRUE
icon_state = "radioactive_area"
@@ -69,11 +69,14 @@
///How much the damage we take from rads is multiplied by.
var/damage_multiplier = 1.0
///If we use a toony glow instead of a more emmissive one.
var/toony = FALSE
dupe_mode = COMPONENT_DUPE_UNIQUE
dupe_type = /datum/component/radiation_effects
/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, radiation_nutrition, radiation_nutrition_cap, glow_toggle, nutrition_toggle)
/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, radiation_nutrition, radiation_nutrition_cap, glow_toggle, nutrition_toggle, toony)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
@@ -115,16 +118,25 @@
if(show_panel)
add_verb(parent, /mob/living/proc/radiation_control_panel)
if(toony)
src.toony = toony
/datum/component/radiation_effects/Destroy(force)
var/atom/movable/parent_movable = parent
if(show_panel)
remove_verb(parent, /mob/living/proc/radiation_control_panel)
if(istype(parent_movable))//For the toony glow.
var/filter = parent_movable.get_filter("rad_glow")
if(filter)
parent_movable.remove_filter("rad_glow")
return ..()
/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))
RegisterSignal(parent, COMSIG_GEIGER_COUNTER_SCAN, PROC_REF(on_geiger_counter_scan))
/datum/component/radiation_effects/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_HANDLE_RADIATION, COMSIG_LIVING_LIFE, COMSIG_LIVING_IRRADIATE_EFFECT))
@@ -136,10 +148,12 @@
if(living_guy.glow_override) //Toggled glow off while we were still actively glowing.
living_guy.glow_override = FALSE
living_guy.set_light(0)
living_guy.remove_filter("rad_glow")
return
if(living_guy.radiation < radiation_glow_threshold)
living_guy.glow_override = FALSE
living_guy.set_light(0)
living_guy.remove_filter("rad_glow")
return
if(glows)
@@ -148,12 +162,16 @@
living_guy.set_light(l_range = light_range, l_power = light_power, l_color = radiation_color, l_on = TRUE)
living_guy.glow_override = TRUE
if(toony)
var/filter = living_guy.get_filter("rad_glow")
if(!filter)
create_toony_glow()
///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)
if((living_guy.radiation > RADIATION_CAP || living_guy.radiation < 0) || (living_guy.accumulated_rads > RADIATION_CAP || living_guy.accumulated_rads < 0))
living_guy.radiation = CLAMP(living_guy.radiation,0,RADIATION_CAP)
living_guy.accumulated_rads = CLAMP(living_guy.accumulated_rads,0,RADIATION_CAP)
@@ -175,7 +193,15 @@
//End of the calculation.
if(contamination && living_guy.radiation > contamination_threshold)
SSradiation.radiate(living_guy, rads * contamination_strength * rad_removal_mod)
//SSradiation.radiate(living_guy, rads * contamination_strength * rad_removal_mod)
radiation_pulse(
living_guy,
max_range = 2,
threshold = RAD_MEDIUM_INSULATION,
chance = CLAMP(rads * contamination_strength, 0, 25),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = rads * contamination_strength
)
///Used for radiation nutrition and healing.
var/rads_to_utilize
@@ -296,6 +322,31 @@
to_chat(parent, span_info("You are [radiation_nutrition ? "now" : "no longer"] gaining nutrition from radiation."))
return FALSE
/datum/component/radiation_effects/proc/create_toony_glow()
var/atom/movable/parent_movable = parent
if (!istype(parent_movable))
return
parent_movable.add_filter("rad_glow", 2, list("type" = "outline", "color" = "#39ff1430", "size" = 2))
addtimer(CALLBACK(src, PROC_REF(toony_glow_loop), parent_movable), rand(0.1 SECONDS, 1.9 SECONDS)) // Things should look uneven
/datum/component/radiation_effects/proc/toony_glow_loop(atom/movable/parent_movable)
var/filter = parent_movable.get_filter("rad_glow")
if (!filter)
return
animate(filter, alpha = 110, time = 1.5 SECONDS, loop = -1)
animate(alpha = 40, time = 2.5 SECONDS)
/datum/component/radiation_effects/proc/on_geiger_counter_scan(mob/living/living_source, mob/user, obj/item/geiger/geiger_counter)
SIGNAL_HANDLER
if(living_source.radiation > 0)
if(contamination && living_source.radiation > contamination_threshold) //Are we spreading radiation?
to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Subject is irradiated and offputting radiation."))
else
to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Subject is irradiated."))
/mob/living/proc/get_radiation_component()
var/datum/component/radiation_effects/rad = GetComponent(/datum/component/radiation_effects)
if(rad)
@@ -0,0 +1,24 @@
/// Marks the item as being radiation protected.
/// Adds the TRAIT_RADIATION_PROTECTED_CLOTHING trait, as well as adding an
/// extra bit to the examine descrpition.
/datum/element/radiation_protected_clothing
/datum/element/radiation_protected_clothing/Attach(datum/target)
. = ..()
if (!isclothing(target))
return ELEMENT_INCOMPATIBLE
ADD_TRAIT(target, TRAIT_RADIATION_PROTECTED_CLOTHING, REF(src))
RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
/datum/element/radiation_protected_clothing/Detach(datum/source, ...)
REMOVE_TRAIT(source, TRAIT_RADIATION_PROTECTED_CLOTHING, REF(src))
UnregisterSignal(source, COMSIG_ATOM_EXAMINE)
return ..()
/datum/element/radiation_protected_clothing/proc/on_examine(datum/source, mob/user, list/examine_text)
SIGNAL_HANDLER
examine_text += span_notice("A patch with a hazmat sign on the side suggests it would <b>protect you from radiation</b>.")
-30
View File
@@ -1,33 +1,3 @@
/datum/looping_sound/geiger
mid_sounds = list(
list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1),
list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1),
list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1),
list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1)
)
mid_length = 1 SECOND
volume = 25
var/last_radiation
/datum/looping_sound/geiger/get_sound(starttime)
var/danger
switch(last_radiation)
if(0 to RAD_LEVEL_MODERATE)
danger = 1
if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH)
danger = 2
if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH)
danger = 3
if(RAD_LEVEL_VERY_HIGH to INFINITY)
danger = 4
else
return null
return ..(starttime, mid_sounds[danger])
/datum/looping_sound/geiger/stop()
. = ..()
last_radiation = 0
/datum/looping_sound/small_motor
start_sound = 'sound/items/small_motor/motor_start_nopull.ogg'
start_length = 2 SECONDS
+3
View File
@@ -60,6 +60,9 @@
/// You will need to manage adding/removing from this yourself, but I'll do the updating for you
var/list/image/update_on_z
/// Radiation insulation types
var/rad_insulation = RAD_NO_INSULATION
/atom/Destroy()
if(reagents)
QDEL_NULL(reagents)
-2
View File
@@ -310,13 +310,11 @@ GLOBAL_LIST_INIT(meteors_catastrophic, list(
meteordrop = /obj/item/ore/uranium
wall_power = 75
/obj/effect/meteor/irradiated/meteor_effect(var/explode)
..()
if(explode)
explosion(src.loc, 0, 0, 4, 3, 0)
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
SSradiation.radiate(src, 50)
// This meteor fries toasters.
/obj/effect/meteor/emp
+1
View File
@@ -197,6 +197,7 @@
light_power = 1
light_color = "#FFFFFF"
light_on = TRUE
rad_insulation = RAD_LIGHT_INSULATION
/obj/structure/atmospheric_retention_field/update_icon()
cut_overlays() //overlays.Cut()
+2
View File
@@ -269,6 +269,7 @@
if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected
occupant.bodytemperature = 261 // Changed to 70 from 140 by Zuhayr due to reoccurance of bug.
unbuckle_mob(occupant, force = TRUE)
//REMOVE_TRAIT(occupant, TRAIT_STASIS, REF(src)) //Stops life almost entirely, so not done here.
occupant = null
current_heat_capacity = initial(current_heat_capacity)
update_use_power(USE_POWER_IDLE)
@@ -297,6 +298,7 @@
if(M.health > -100 && (M.health < 0 || M.sleeping))
to_chat(M, span_boldnotice("You feel a cold liquid surround you. Your skin starts to freeze up."))
occupant = M
//ADD_TRAIT(occupant, TRAIT_STASIS, REF(src)) //Stops life almost entirely, so not done here.
buckle_mob(occupant, forced = TRUE, check_loc = FALSE)
vis_contents |= occupant
occupant.pixel_y += 19
+1
View File
@@ -61,6 +61,7 @@
var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg'
var/knock_unpowered_sound = 'sound/machines/door/knock_glass.ogg'
var/mob/hold_open
rad_insulation = RAD_MEDIUM_INSULATION
// Frozen airlocks and how to deice them
var/frozen = FALSE
+19 -7
View File
@@ -381,14 +381,26 @@
icon = 'icons/obj/doors/Dooruranium.dmi'
mineral = MAT_URANIUM
var/last_event = 0
var/rad_power = 7.5
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/machinery/door/airlock/uranium/process()
if(world.time > last_event+20)
if(prob(50))
SSradiation.radiate(src, rad_power)
last_event = world.time
..()
/obj/machinery/door/airlock/uranium/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 3,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 5,
)
last_event = world.time
active = FALSE
/obj/machinery/door/airlock/uranium_appearance
icon = 'icons/obj/doors/Dooruranium.dmi'
+2 -1
View File
@@ -64,7 +64,6 @@
icon_state = icon_state_closed
else
icon_state = icon_state_open
SSradiation.resistance_cache.Remove(get_turf(src))
return
// Proc: emag_act()
@@ -90,6 +89,7 @@
update_nearby_tiles()
update_icon()
set_opacity(0)
rad_insulation = RAD_NO_INSULATION
addtimer(CALLBACK(src, PROC_REF(complete_force_open)), 1.5 SECONDS, TIMER_DELETE_ME|TIMER_UNIQUE)
/obj/machinery/door/blast/proc/complete_force_open()
@@ -112,6 +112,7 @@
density = TRUE
update_nearby_tiles()
update_icon()
rad_insulation = RAD_EXTREME_INSULATION
if(istransparent)
set_opacity(0)
else
-1
View File
@@ -448,7 +448,6 @@
icon_state = "door1"
else
icon_state = "door0"
SSradiation.resistance_cache.Remove(get_turf(src))
return
+8 -1
View File
@@ -114,9 +114,16 @@
..()
if(phasing)
phasing = FALSE
SSradiation.radiate(get_turf(src), 30)
radiation_pulse(
src,
max_range = 7,
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE * 5,
strength = 250
)
log_append_to_last("WARNING: BLUESPACE DRIVE INSTABILITY DETECTED. DISABLING DRIVE.",1)
visible_message(span_alien("The [src.name] appears to flicker, before its silhouette stabilizes!"))
return
/obj/mecha/combat/phazon/janus/dynbulletdamage(var/obj/item/projectile/Proj)
+7 -1
View File
@@ -138,7 +138,13 @@
/obj/item/mecha_parts/mecha_equipment/generator/nuclear/process()
if(..())
SSradiation.radiate(src, (rad_per_cycle * 3))
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = 25
)
return
/obj/item/mecha_parts/mecha_equipment/generator/nuclear/critfail()
@@ -23,10 +23,40 @@
qdel(src)
/obj/effect/decal/cleanable/greenglow
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/effect/decal/cleanable/greenglow/Initialize(mapload, _age)
. = ..()
QDEL_IN(src, 2 MINUTES)
START_PROCESSING(SSobj, src)
/obj/effect/decal/cleanable/greenglow/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/effect/decal/cleanable/greenglow/process()
radiate()
..()
/obj/effect/decal/cleanable/greenglow/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 5,
threshold = RAD_LIGHT_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = 2
)
last_event = world.time
active = FALSE
/obj/effect/decal/cleanable/dirt
name = "dirt"
@@ -2,7 +2,35 @@
/obj/effect/map_effect/radiation_emitter
name = "radiation emitter"
icon_state = "radiation_emitter"
var/range = 3
var/radiation_power = 30 // Bigger numbers means more radiation.
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
var/strength = 50
/obj/effect/map_effect/radiation_emitter/process()
radiate()
..()
/obj/effect/map_effect/radiation_emitter/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = range,
threshold = RAD_LIGHT_INSULATION,
chance = radiation_power,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = strength
)
last_event = world.time
active = FALSE
/obj/effect/map_effect/radiation_emitter/Initialize(mapload)
START_PROCESSING(SSobj, src)
@@ -12,8 +40,7 @@
STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/map_effect/radiation_emitter/process()
SSradiation.radiate(src, radiation_power)
/obj/effect/map_effect/radiation_emitter/strong
range = 7
radiation_power = 100
strength = 250
+2
View File
@@ -195,6 +195,7 @@
syringe = null
/obj/structure/closet/body_bag/cryobag/Entered(atom/movable/AM)
ADD_TRAIT(AM, TRAIT_STASIS, REF(src))
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
H.Stasis(stasis_level)
@@ -209,6 +210,7 @@
..()
/obj/structure/closet/body_bag/cryobag/Exited(atom/movable/AM)
REMOVE_TRAIT(AM, TRAIT_STASIS, REF(src))
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
H.Stasis(0)
+18 -2
View File
@@ -575,6 +575,9 @@
/obj/item/shockpaddles/standalone
desc = "A pair of shockpaddles powered by an experimental miniaturized reactor" //Inspired by the advanced e-gun
var/fail_counter = 0
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/shockpaddles/standalone/Destroy()
. = ..()
@@ -585,12 +588,25 @@
return 1
/obj/item/shockpaddles/standalone/checked_use(var/charge_amt)
SSradiation.radiate(src, charge_amt/12) //just a little bit of radiation. It's the price you pay for being powered by magic I guess
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = 50
)
return 1
/obj/item/shockpaddles/standalone/process()
if(fail_counter > 0)
SSradiation.radiate(src, fail_counter--)
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = 15
)
fail_counter--
else
STOP_PROCESSING(SSobj, src)
+118 -101
View File
@@ -1,102 +1,136 @@
//Geiger counter
//Rewritten version of TG's geiger counter
//I opted to show exact radiation levels
/obj/item/geiger
name = "geiger counter"
desc = "A handheld device used for detecting and measuring radiation in an area."
icon = 'icons/obj/device.dmi'
/obj/item/geiger //DISCLAIMER: I know nothing about how real-life Geiger counters work. This will not be realistic. ~Xhuis
name = "\improper Geiger counter"
desc = "A handheld device used for detecting and measuring radiation pulses."
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "geiger_off"
item_state = "multitool"
item_icons = list(
slot_l_hand_str = 'icons/mob/inhands/equipment/tools_lefthand.dmi',
slot_r_hand_str = 'icons/mob/inhands/equipment/tools_righthand.dmi',
)
w_class = ITEMSIZE_SMALL
var/scanning = 0
var/radiation_count = 0
var/datum/looping_sound/geiger/soundloop
slot_flags = SLOT_BELT
item_flags = NOBLUDGEON
matter = list(/datum/material/steel = SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.5)
var/last_perceived_radiation_danger = null
var/scanning = FALSE
var/mounted = FALSE
pickup_sound = 'sound/items/pickup/device.ogg'
drop_sound = 'sound/items/drop/device.ogg'
/obj/item/geiger/Initialize(mapload)
soundloop = new(list(src), FALSE)
return ..()
. = ..()
/obj/item/geiger/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(soundloop)
return ..()
/obj/item/geiger/process()
get_radiation()
/obj/item/geiger/proc/get_radiation()
if(!scanning)
return
radiation_count = SSradiation.get_rads_at_turf(get_turf(src))
update_icon()
update_sound()
RegisterSignal(src, COMSIG_IN_RANGE_OF_IRRADIATION, PROC_REF(on_pre_potential_irradiation))
/obj/item/geiger/examine(mob/user)
. = ..()
get_radiation()
. += span_warning("[scanning ? "Ambient" : "Stored"] radiation level: [radiation_count ? radiation_count : "0"]Bq.")
/obj/item/geiger/rad_act(amount)
if(!amount || !scanning)
return FALSE
if(amount > radiation_count)
radiation_count = amount
update_icon()
update_sound()
/obj/item/geiger/proc/update_sound()
var/datum/looping_sound/geiger/loop = soundloop
if(!scanning)
loop.stop()
return
if(!radiation_count)
loop.stop()
return
loop.last_radiation = radiation_count
loop.start()
/obj/item/geiger/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
if(mounted)
return
scanning = !scanning
if(scanning)
START_PROCESSING(SSobj, src)
else
STOP_PROCESSING(SSobj, src)
update_icon()
update_sound()
to_chat(user, span_notice("[icon2html(src, user.client)] You switch [scanning ? "on" : "off"] \the [src]."))
. += span_info("Alt-click it to clear stored radiation levels.")
switch(last_perceived_radiation_danger)
if(null)
. += span_notice("Ambient radiation level count reports that all is well.")
if(PERCEIVED_RADIATION_DANGER_LOW)
. += span_alert("Ambient radiation levels slightly above average.")
if(PERCEIVED_RADIATION_DANGER_MEDIUM)
. += span_warning("Ambient radiation levels above average.")
if(PERCEIVED_RADIATION_DANGER_HIGH)
. += span_danger("Ambient radiation levels highly above average.")
if(PERCEIVED_RADIATION_DANGER_EXTREME)
. += span_suicide("Ambient radiation levels reaching critical level!")
/obj/item/geiger/update_icon()
if(!scanning)
icon_state = "geiger_off"
return 1
return ..()
switch(radiation_count)
switch(last_perceived_radiation_danger)
if(null)
icon_state = "geiger_on_1"
if(-INFINITY to RAD_LEVEL_LOW)
icon_state = "geiger_on_1"
if(RAD_LEVEL_LOW to RAD_LEVEL_MODERATE)
if(PERCEIVED_RADIATION_DANGER_LOW)
icon_state = "geiger_on_2"
if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH)
if(PERCEIVED_RADIATION_DANGER_MEDIUM)
icon_state = "geiger_on_3"
if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH)
if(PERCEIVED_RADIATION_DANGER_HIGH)
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH to INFINITY)
if(PERCEIVED_RADIATION_DANGER_EXTREME)
icon_state = "geiger_on_5"
return ..()
///////////////////////
/obj/item/geiger/attack_self(mob/user)
. = ..()
if(.)
return TRUE
scanning = !scanning
if (scanning)
AddComponent(/datum/component/geiger_sound)
else
qdel(GetComponent(/datum/component/geiger_sound))
update_icon()
balloon_alert(user, "switch [scanning ? "on" : "off"]")
/obj/item/geiger/afterattack(atom/interacting_with, mob/user, proximity_flag, click_parameters)
. = ..()
if(SHOULD_SKIP_INTERACTION(interacting_with, src, user))
return NONE
return attack_at_range(interacting_with, user, proximity_flag, click_parameters)
/obj/item/geiger/proc/attack_at_range(atom/interacting_with, mob/living/user, proximity_flag, click_parameters) //This is ranged_interact_with_atom on TG but we don't have that yet.
if(!CAN_IRRADIATE(interacting_with))
return NONE
user.visible_message(span_notice("[user] scans [interacting_with] with [src]."), span_notice("You scan [interacting_with]'s radiation levels with [src]..."))
addtimer(CALLBACK(src, PROC_REF(scan), interacting_with, user), 20, TIMER_UNIQUE) // Let's not have spamming GetAllContents
return ITEM_INTERACT_SUCCESS
/obj/item/geiger/equipped(mob/user, slot, initial)
. = ..()
RegisterSignal(user, COMSIG_IN_RANGE_OF_IRRADIATION, PROC_REF(on_pre_potential_irradiation))
/obj/item/geiger/dropped(mob/user, silent = FALSE)
. = ..()
UnregisterSignal(user, COMSIG_IN_RANGE_OF_IRRADIATION)
/obj/item/geiger/proc/on_pre_potential_irradiation(datum/source, datum/radiation_pulse_information/pulse_information, insulation_to_target)
SIGNAL_HANDLER
last_perceived_radiation_danger = get_perceived_radiation_danger(pulse_information, insulation_to_target)
addtimer(CALLBACK(src, PROC_REF(reset_perceived_danger)), TIME_WITHOUT_RADIATION_BEFORE_RESET, TIMER_UNIQUE | TIMER_OVERRIDE)
if (scanning)
update_icon()
/obj/item/geiger/proc/reset_perceived_danger()
last_perceived_radiation_danger = null
if (scanning)
update_icon()
/obj/item/geiger/proc/scan(atom/target, mob/user)
if (SEND_SIGNAL(target, COMSIG_GEIGER_COUNTER_SCAN, user, src) & COMSIG_GEIGER_COUNTER_SCAN_SUCCESSFUL)
return
if(isliving(target))
var/mob/living/living_target = target
if(living_target.radiation)
to_chat(user, span_notice("[icon2html(src, user)] [living_target] is reporting a radiation level of [living_target.radiation]."))
return
to_chat(user, span_notice("[icon2html(src, user)] [isliving(target) ? "Subject" : "Target"] is free of radioactive contamination."))
/obj/item/geiger/click_alt(mob/living/user)
if(!scanning)
to_chat(user, span_warning("[src] must be on to reset its radiation level!"))
return CLICK_ACTION_BLOCKING
to_chat(user, span_notice("You flush [src]'s radiation counts, resetting it to normal."))
last_perceived_radiation_danger = null
update_icon()
return CLICK_ACTION_SUCCESS
/obj/item/geiger/wall
name = "mounted geiger counter"
@@ -105,8 +139,7 @@
icon_state = "geiger_wall"
item_state = "geiger_wall"
anchored = TRUE
scanning = 1
radiation_count = 0
scanning = TRUE
plane = TURF_PLANE
layer = ABOVE_TURF_LAYER
w_class = ITEMSIZE_LARGE
@@ -118,41 +151,25 @@
mounted = TRUE
/obj/item/geiger/wall/Initialize(mapload)
START_PROCESSING(SSobj, src)
soundloop = new(list(src), FALSE)
return ..()
. = ..()
if(scanning)
AddComponent(/datum/component/geiger_sound)
/obj/item/geiger/wall/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(soundloop)
return ..()
/obj/item/geiger/wall/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
scanning = !scanning
update_icon()
update_sound()
to_chat(user, span_notice("[icon2html(src, user.client)] You switch [scanning ? "on" : "off"] \the [src]."))
/obj/item/geiger/wall/update_icon()
if(!scanning)
icon_state = "geiger_wall-p"
return 1
switch(radiation_count)
switch(last_perceived_radiation_danger)
if(null)
icon_state = "geiger_level_1"
if(-INFINITY to RAD_LEVEL_LOW)
icon_state = "geiger_level_1"
if(RAD_LEVEL_LOW to RAD_LEVEL_MODERATE)
if(PERCEIVED_RADIATION_DANGER_LOW)
icon_state = "geiger_level_2"
if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH)
if(PERCEIVED_RADIATION_DANGER_MEDIUM)
icon_state = "geiger_level_3"
if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH)
if(PERCEIVED_RADIATION_DANGER_HIGH)
icon_state = "geiger_level_4"
if(RAD_LEVEL_VERY_HIGH to INFINITY)
if(PERCEIVED_RADIATION_DANGER_EXTREME)
icon_state = "geiger_level_5"
/obj/item/geiger/wall/attack_ai(mob/user as mob)
+65 -2
View File
@@ -27,18 +27,58 @@
name = "misshapen manhole cover"
desc = "The top of this twisted chunk of metal is faintly stamped with a five pointed star. 'Property of US Army, Pascal B - 1957'."
catalogue_data = list(/datum/category_item/catalogue/information/objects/pascalb)
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/poi/pascalb/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/poi/pascalb/process()
SSradiation.radiate(src, 5)
radiate()
..()
/obj/item/poi/pascalb/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 3,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 25
)
last_event = world.time
active = FALSE
/obj/item/poi/pascalb/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/poi/pascalb/deadly //For testing purposes, mainly.
/obj/item/poi/pascalb/deadly/radiate()
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 3,
threshold = RAD_MEDIUM_INSULATION,
chance = 100,
strength = 250
)
last_event = world.time
active = FALSE
/datum/category_item/catalogue/information/objects/oldreactor
name = "Object - Zorren Fission Reactor Rack"
desc = "Prior to Humanity's arrival in the virgo-erigone space, introducing advanced phoron technologies, \
@@ -78,15 +118,38 @@
name = "ruptured fission reactor rack"
desc = "This broken hunk of machinery looks extremely dangerous."
catalogue_data = list(/datum/category_item/catalogue/information/objects/oldreactor)
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/poi/brokenoldreactor/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/poi/brokenoldreactor/process()
SSradiation.radiate(src, 25)
radiate()
..()
/obj/item/poi/brokenoldreactor/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 50
)
last_event = world.time
active = FALSE
/obj/item/poi/brokenoldreactor/Destroy()
UnregisterSignal(src, COMSIG_ATOM_PROPAGATE_RAD_PULSE)
STOP_PROCESSING(SSobj, src)
return ..()
@@ -1100,9 +1100,13 @@ CIGARETTE PACKETS ARE IN FANCY.DM
user.drop_from_inventory(e)
log_and_message_admins("[user] dusted themselves and caused massive radiation with [src]!",user)
user.dust()
var/rads = 500
SSradiation.radiate(src, rads)
radiation_pulse(
src,
max_range = 12,
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE * 2,
strength = 300
)
set_light(5)
START_PROCESSING(SSobj, src)
else
+10 -2
View File
@@ -18,6 +18,7 @@
var/upgrading = FALSE
var/applies_material_colour = 1
var/wall_type = /turf/simulated/wall
rad_insulation = RAD_VERY_LIGHT_INSULATION
/obj/structure/girder/Initialize(mapload, var/material_key)
. = ..()
@@ -42,9 +43,16 @@
/obj/structure/girder/proc/radiate()
var/total_radiation = girder_material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0)
if(!total_radiation)
return
return FALSE
SSradiation.radiate(src, total_radiation)
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = total_radiation
)
return total_radiation
+2
View File
@@ -59,6 +59,7 @@
density = FALSE
layer = ABOVE_TURF_LAYER
can_atmos_pass = ATMOS_PASS_NO
rad_insulation = RAD_LIGHT_INSULATION
alpha = 150
/obj/structure/holosign/barrier/combifan/Destroy()
@@ -75,6 +76,7 @@
icon_state = "holo_medical"
alpha = 125
var/buzzed = 0
rad_insulation = RAD_NO_INSULATION
/obj/structure/holosign/barrier/medical/CanPass(atom/movable/mover, border_dir)
. = ..()
+44 -1
View File
@@ -23,6 +23,7 @@
var/can_pick = TRUE //can it be picked/bypassed?
var/lock_difficulty = 1 //multiplier to picking/bypassing time
var/keysound = 'sound/items/toolbelt_equip.ogg'
rad_insulation = RAD_MEDIUM_INSULATION
/obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
TemperatureAct(exposed_temperature)
@@ -240,19 +241,57 @@
/obj/structure/simple_door/process()
if(!material.radioactivity)
return
SSradiation.radiate(src, round(material.radioactivity/3))
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = round((material.radioactivity * 0.33), 0.1),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = material.radioactivity
)
/obj/structure/simple_door/iron/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_IRON)
/obj/structure/simple_door/silver
rad_insulation = RAD_HEAVY_INSULATION
/obj/structure/simple_door/silver/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_SILVER)
/obj/structure/simple_door/gold
rad_insulation = RAD_HEAVY_INSULATION
/obj/structure/simple_door/gold/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_GOLD)
/obj/structure/simple_door/uranium
rad_insulation = RAD_NO_INSULATION
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/structure/simple_door/uranium/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_URANIUM)
START_PROCESSING(SSobj, src)
/obj/structure/simple_door/uranium/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 3,
threshold = RAD_LIGHT_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 5
)
last_event = world.time
active = FALSE
/obj/structure/simple_door/sandstone/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_SANDSTONE)
@@ -260,9 +299,13 @@
/obj/structure/simple_door/phoron/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_PHORON)
/obj/structure/simple_door/diamond
rad_insulation = RAD_EXTREME_INSULATION
/obj/structure/simple_door/diamond/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_DIAMOND)
//I was going to give wooden doors RAD_VERY_LIGHT_INSULATION but they need a proper parent instead of this garbage.
/obj/structure/simple_door/wood/Initialize(mapload,var/material_name)
. = ..(mapload, material_name || MAT_WOOD)
knock_sound = 'sound/machines/door/knock_wood.wav'
+1 -2
View File
@@ -261,10 +261,8 @@
//Yes, showers are super powerful as far as washing goes.
/obj/machinery/shower/proc/wash_atom(atom/A)
/* //TG cleans rads and only does CLEAN_WASH, not sure if that's wanted here.
A.wash(CLEAN_RAD | CLEAN_TYPE_WEAK) // Clean radiation non-instantly
A.wash(CLEAN_WASH)
*/
A.wash(CLEAN_SCRUB)
reagents.splash(A, reaction_volume / 20, 1, TRUE, min_spill = 0, max_spill = 0) //Reaction volume needs to be divided by 20 due to a larger internal volume
@@ -274,6 +272,7 @@
check_heat(L)
L.extinguish_mob()
L.adjust_fire_stacks(-20) //Douse ourselves with water to avoid fire more easily
L.radiation = CLAMP(L.radiation - 5, 0, RADIATION_CAP)
if(!iscarbon(A))
return
+5
View File
@@ -23,6 +23,7 @@
var/glasstype = null // Set this in subtypes. Null is assumed strange or otherwise impossible to dismantle, such as for shuttle glass.
var/silicate = 0 // number of units of silicate
var/fulltile = FALSE // Set to true on full-tile variants.
rad_insulation = RAD_VERY_LIGHT_INSULATION //Windows can have multiple placed on one tile, meaning you have to account for the potential that someone could just build a bunch of windows on one tile to prevent rads entirely.
/obj/structure/window/examine(mob/user)
. = ..()
@@ -514,6 +515,7 @@
maxhealth = 80
fulltile = TRUE
flags = NONE
rad_insulation = RAD_LIGHT_INSULATION
/obj/structure/window/phoronreinforced
name = "reinforced borosilicate window"
@@ -527,12 +529,14 @@
damage_per_fire_tick = 1.0 // This should last for 80 fire ticks if the window is not damaged at all. The idea is that borosilicate windows have something like ablative layer that protects them for a while.
maxhealth = 80.0
force_threshold = 10
rad_insulation = RAD_LIGHT_INSULATION
/obj/structure/window/phoronreinforced/full
icon_state = "phoronrwindow-full"
maxhealth = 160
fulltile = TRUE
flags = NONE
rad_insulation = RAD_MEDIUM_INSULATION
/obj/structure/window/reinforced
name = "reinforced window"
@@ -578,6 +582,7 @@
basestate = "w"
dir = 5
force_threshold = 7
rad_insulation = RAD_MEDIUM_INSULATION
/obj/structure/window/reinforced/polarized
name = "electrochromic window"
@@ -11,6 +11,7 @@
damage_per_fire_tick = 1.0
maxhealth = 100.0
force_threshold = 10
rad_insulation = RAD_EXTREME_INSULATION
/obj/structure/window/titanium/full
icon_state = "window-full"
@@ -30,6 +31,7 @@
damage_per_fire_tick = 1.0
maxhealth = 120.0
force_threshold = 10
rad_insulation = RAD_EXTREME_INSULATION
/obj/structure/window/plastitanium/full
icon_state = "window-full"
+4
View File
@@ -306,6 +306,10 @@
soundin = pick(
'sound/effects/mech/powerloader_step.ogg',
'sound/effects/mech/powerloader_step2.ogg')
if ("sizzle")
soundin = pick(
'sound/effects/wounds/sizzle1.ogg',
'sound/effects/wounds/sizzle2.ogg')
return soundin
+31
View File
@@ -91,6 +91,7 @@
var/join_flags = 0 //Bitstring to represent adjacency of joining walls
var/join_group = "shuttle" //A tag for what other walls to join with. Null if you don't want them to.
var/static/list/antilight_cache
rad_insulation = RAD_MEDIUM_INSULATION
/turf/simulated/shuttle/Initialize(mapload)
. = ..()
@@ -312,6 +313,36 @@
/turf/simulated/floor/tiled/material/uranium
icon_state = "uranium"
initial_flooring = /datum/decl/flooring/tiling/material/uranium
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/turf/simulated/floor/tiled/material/uranium/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_ATOM_PROPAGATE_RAD_PULSE, PROC_REF(radiate))
/turf/simulated/floor/tiled/material/uranium/Destroy()
UnregisterSignal(src, COMSIG_ATOM_PROPAGATE_RAD_PULSE)
. = ..()
/turf/simulated/floor/tiled/material/uranium/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 1,
threshold = RAD_LIGHT_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 1
)
propagate_radiation_pulse()
last_event = world.time
active = FALSE
/datum/decl/flooring/tiling/material/uranium
name = "uranium floor"
@@ -4,8 +4,6 @@
if(can_open == WALL_OPENING)
return
SSradiation.resistance_cache.Remove(src)
if(density)
can_open = WALL_OPENING
//flick("[material.icon_base]fwall_opening", src)
-1
View File
@@ -26,7 +26,6 @@
else if(material.opacity < 0.5 && opacity)
set_light(0)
SSradiation.resistance_cache.Remove(src)
update_connections(1)
update_icon()
+28
View File
@@ -1,5 +1,6 @@
/turf/simulated/wall/r_wall
icon_state = "rgeneric"
rad_insulation = RAD_HEAVY_INSULATION
/turf/simulated/wall/r_wall/Initialize(mapload)
. = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) //3strong
@@ -9,30 +10,44 @@
. = ..(mapload, MAT_STEELHULL, null, MAT_STEELHULL)
/turf/simulated/wall/rshull
icon_state = "hull-r_steel"
rad_insulation = RAD_HEAVY_INSULATION
/turf/simulated/wall/rshull/Initialize(mapload)
. = ..(mapload, MAT_STEELHULL, MAT_STEELHULL, MAT_STEELHULL)
/turf/simulated/wall/pshull
icon_state = "hull-plasteel"
rad_insulation = RAD_HEAVY_INSULATION
/turf/simulated/wall/pshull/Initialize(mapload) //Spaaaace-er ship.
. = ..(mapload, MAT_PLASTEELHULL, null, MAT_PLASTEELHULL)
/turf/simulated/wall/rpshull
icon_state = "hull-r_plasteel"
/turf/simulated/wall/rpshull/Initialize(mapload)
. = ..(mapload, MAT_PLASTEELHULL, MAT_PLASTEELHULL, MAT_PLASTEELHULL)
/turf/simulated/wall/dshull
icon_state = "hull-durasteel"
rad_insulation = RAD_HEAVY_INSULATION
/turf/simulated/wall/dshull/Initialize(mapload) //Spaaaace-est ship.
. = ..(mapload, MAT_DURASTEELHULL, null, MAT_DURASTEELHULL)
/turf/simulated/wall/rdshull
icon_state = "hull-r_durasteel"
rad_insulation = RAD_EXTREME_INSULATION
/turf/simulated/wall/rdshull/Initialize(mapload)
. = ..(mapload, MAT_DURASTEELHULL, MAT_DURASTEELHULL, MAT_DURASTEELHULL)
/turf/simulated/wall/thull
icon_state = "hull-titanium"
rad_insulation = RAD_HEAVY_INSULATION
/turf/simulated/wall/thull/Initialize(mapload)
. = ..(mapload, MAT_TITANIUMHULL, null, MAT_TITANIUMHULL)
/turf/simulated/wall/rthull
icon_state = "hull-r_titanium"
rad_insulation = RAD_EXTREME_INSULATION
/turf/simulated/wall/rthull/Initialize(mapload)
. = ..(mapload, MAT_TITANIUMHULL, MAT_TITANIUMHULL, MAT_TITANIUMHULL)
@@ -56,8 +71,16 @@
. = ..(mapload, MAT_GOLD)
/turf/simulated/wall/silver/Initialize(mapload)
. = ..(mapload, MAT_SILVER)
/turf/simulated/wall/lead
rad_insulation = RAD_EXTREME_INSULATION
/turf/simulated/wall/lead/Initialize(mapload)
. = ..(mapload, MAT_LEAD)
/turf/simulated/wall/r_lead
rad_insulation = RAD_EXTREME_INSULATION
/turf/simulated/wall/r_lead/Initialize(mapload)
. = ..(mapload, MAT_LEAD, MAT_LEAD)
/turf/simulated/wall/phoron/Initialize(mapload)
@@ -80,11 +103,13 @@
/turf/simulated/wall/concrete
icon_state = "brick"
rad_insulation = RAD_HEAVY_INSULATION
/turf/simulated/wall/concrete/Initialize(mapload)
. = ..(mapload, MAT_CONCRETE) //3strong
/turf/simulated/wall/r_concrete
rad_insulation = RAD_HEAVY_INSULATION
icon_state = "rbrick"
/turf/simulated/wall/r_concrete/Initialize(mapload)
@@ -98,6 +123,9 @@
/turf/simulated/wall/titanium/Initialize(mapload)
. = ..(mapload, MAT_TITANIUM)
/turf/simulated/wall/durasteel
rad_insulation = RAD_HEAVY_INSULATION
/turf/simulated/wall/durasteel/Initialize(mapload)
. = ..(mapload, MAT_DURASTEEL, MAT_DURASTEEL)
@@ -215,6 +215,33 @@ GLOBAL_LIST_EMPTY(flesh_overlay_cache)
/turf/simulated/wall/uranium
icon_state = "uranium"
icon = 'icons/turf/wall_masks_vr.dmi'
var/last_event = 0
/turf/simulated/wall/uranium/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_ATOM_PROPAGATE_RAD_PULSE, PROC_REF(radiate))
/turf/simulated/wall/uranium/Destroy()
UnregisterSignal(src, COMSIG_ATOM_PROPAGATE_RAD_PULSE)
. = ..()
/turf/simulated/wall/uranium/radiate()
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 3,
threshold = RAD_LIGHT_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 5
)
propagate_radiation_pulse()
last_event = world.time
active = FALSE
/turf/simulated/wall/virgo2
icon_state = "virgo2"
+10 -1
View File
@@ -23,6 +23,7 @@
// There's basically always going to be wall connections, making this lazy doesn't seem like it'd help much unless you wanted to make it bitflags instead.
var/list/wall_connections = list("0", "0", "0", "0")
rad_insulation = RAD_MEDIUM_INSULATION
// Walls always hide the stuff below them.
/turf/simulated/wall/levelupdate()
@@ -297,11 +298,19 @@
return
/turf/simulated/wall/proc/radiate()
SIGNAL_HANDLER
var/total_radiation = material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) + (girder_material ? girder_material.radioactivity / 2 : 0)
if(!total_radiation)
return
SSradiation.radiate(src, total_radiation)
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = total_radiation,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = total_radiation
)
return total_radiation
/turf/simulated/wall/proc/burn(temperature)
@@ -21,7 +21,20 @@
attack_verb = "splashes"
/datum/blob_type/radioactive_ooze/on_pulse(var/obj/structure/blob/B)
SSradiation.radiate(B, 200)
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = 75
)
/datum/blob_type/radioactive_ooze/on_chunk_tick(obj/item/blobcore_chunk/B)
SSradiation.radiate(B, rand(25,100))
radiation_pulse(
src,
max_range = 3,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE * 0.5,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 25
)
@@ -34,17 +34,26 @@
var/obj/item/clothing/head/helmet/space/rig/helmet = holder.helmet
var/obj/item/clothing/gloves/gauntlets/rig/gloves = holder.gloves
var/list/items_to_update = list()
to_chat(H, span_boldnotice("You activate your suit's powered radiation shielding."))
stored_rad_armor = holder.armor["rad"]
if(boots)
boots.armor["rad"] = 100
items_to_update += boots
if(chest)
chest.armor["rad"] = 100
items_to_update += chest
if(helmet)
helmet.armor["rad"] = 100
items_to_update += helmet
if(gloves)
gloves.armor["rad"] = 100
items_to_update += gloves
holder.armor["rad"] = 100
items_to_update += holder
for(var/obj/item/part in items_to_update)
ADD_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT)
/obj/item/rig_module/rad_shield/deactivate()
@@ -57,20 +66,30 @@
var/obj/item/clothing/head/helmet/space/rig/helmet = holder.helmet
var/obj/item/clothing/gloves/gauntlets/rig/gloves = holder.gloves
var/list/items_to_update = list()
to_chat(H, span_danger("You deactivate your suit's powered radiation shielding."))
if(boots)
boots.armor["rad"] = stored_rad_armor
items_to_update += boots
if(chest)
chest.armor["rad"] = stored_rad_armor
items_to_update += chest
if(helmet)
helmet.armor["rad"] = stored_rad_armor
items_to_update += helmet
if(gloves)
gloves.armor["rad"] = stored_rad_armor
items_to_update += gloves
holder.armor["rad"] = stored_rad_armor
items_to_update += holder
stored_rad_armor = 0
for(var/obj/item/part in items_to_update)
REMOVE_TRAIT(part, TRAIT_RADIATION_PROTECTED_CLOTHING, MOD_TRAIT)
/obj/item/rig_module/rad_shield/advanced
name = "advanced radiation absorption device"
desc = "The acronym of this device - R.A.D. - and its full name both convey the application of the module. It has a changelog inscribed \
+9
View File
@@ -88,6 +88,11 @@
body_parts_covered = HEAD|FACE|EYES
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100)
/obj/item/clothing/head/radiation/Initialize(mapload)
. = ..()
AddElement(/datum/element/radiation_protected_clothing)
/obj/item/clothing/suit/radiation
name = "Radiation suit"
desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation."
@@ -102,6 +107,10 @@
flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
item_flags = THICKMATERIAL
/obj/item/clothing/suit/radiation/Initialize(mapload)
. = ..()
AddElement(/datum/element/radiation_protected_clothing)
/obj/item/clothing/suit/radiation/teshari
name = "Small radiation suit"
desc = "A specialist suit that protects against radiation, designed specifically for use by Teshari. Made to order by Aether."
+32
View File
@@ -96,6 +96,38 @@
desc = "A " + MAT_URANIUM + " coin. You probably don't want to store this in your pants pocket..."
icon_state = "coin_uranium"
matter = list(MAT_URANIUM = 250)
var/last_event = 0
var/active = 0
/obj/item/coin/uranium/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/coin/uranium/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/coin/uranium/process()
radiate()
..()
/obj/item/coin/uranium/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 1,
threshold = RAD_LIGHT_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 2
)
last_event = world.time
active = FALSE
/obj/item/coin/platinum
name = MAT_PLATINUM + " coin"
+13 -1
View File
@@ -29,9 +29,21 @@
else if(activeFor == leaveBelt)
GLOB.command_announcement.Announce("The [using_map.facility_type] has passed the radiation belt. Please allow for up to one minute while radiation levels dissipate, and report to medbay if you experience any unusual symptoms. Maintenance will lose all access again shortly.", "Anomaly Alert")
/datum/event/radiation_storm/proc/radiate()
//This sucks. Just mutate.
/*
var/radiation_level = rand(15, 35)
for(var/z in using_map.station_levels)
SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1)
var/turf/epicentre = locate(round(world.maxx / 2), round(world.maxy / 2), z)
if(epicentre)
radiation_pulse(
epicentre,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
)
*/
for(var/mob/living/carbon/C in GLOB.living_mob_list)
if(!(C.z in using_map.station_levels) || C.isSynthetic() || isbelly(C.loc))
+7 -1
View File
@@ -37,7 +37,13 @@
continue
//Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation.
L.rad_act(rand(15, 30))
radiation_pulse(
L,
max_range = 1,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = rand(10, 50)
)
/datum/event/solar_storm/end()
GLOB.command_announcement.Announce("The solar storm has passed the [using_map.facility_type]. It is now safe to resume EVA activities. Please report to medbay if you experience any unusual symptoms. ", "Anomaly Alert")
@@ -29,9 +29,41 @@
radiate()
/datum/event2/event/radiation_storm/proc/radiate()
//This sucks. Just mutate.
/*
var/radiation_level = rand(15, 35)
for(var/z in using_map.station_levels)
SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1)
var/turf/epicentre = locate(round(world.maxx / 2), round(world.maxy / 2), z)
if(epicentre)
radiation_pulse(
epicentre,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
)
*/
for(var/mob/living/carbon/C in GLOB.living_mob_list)
if(!(C.z in using_map.station_levels) || C.isSynthetic() || isbelly(C.loc))
continue
var/area/A = get_area(C)
if(!A)
continue
if(A.flag_check(RAD_SHIELDED))
continue
if(ishuman(C))
var/mob/living/carbon/human/H = C
var/chance = 5.0
chance -= (chance / 100) * C.getarmor(null, "rad")
if(prob(chance))
if (prob(75))
randmutb(H) // Applies bad mutation
domutcheck(H,null,MUTCHK_FORCED)
else
randmutg(H) // Applies good mutation
domutcheck(H,null,MUTCHK_FORCED)
H.UpdateAppearance()
/datum/event2/event/radiation_storm/end()
GLOB.command_announcement.Announce("The station has passed the radiation belt. \
@@ -49,4 +49,10 @@
continue
//Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation.
L.rad_act(rand(15, 30))
radiation_pulse(
L,
max_range = 1,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = rand(10, 50)
)
+42 -3
View File
@@ -5,6 +5,40 @@
item_state = "diamond"
default_type = MAT_SUPERMATTER
apply_colour = TRUE
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/stack/material/supermatter/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/stack/material/supermatter/process()
radiate()
..()
/obj/item/stack/material/supermatter/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = (amount * 0.2), //10 range at 50 amount
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = NEBULA_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = amount * 0.5 //2 sheets = 1 rad, 50 sheets = 25 rads.
)
last_event = world.time
active = FALSE
/obj/item/stack/material/supermatter/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/stack/material/supermatter/proc/update_mass() // Due to how dangerous they can be, the item will get heavier and larger the more are in the stack.
slowdown = amount / 10
@@ -20,7 +54,6 @@
. = ..()
update_mass()
SSradiation.radiate(src, 5 + amount)
var/mob/living/M = user
if(!istype(M))
return
@@ -47,9 +80,15 @@
/obj/item/stack/material/supermatter/ex_act(severity) // An incredibly hard to manufacture material, SM chunks are unstable by their 'stabilized' nature.
if(prob((4 / severity) * 20))
SSradiation.radiate(get_turf(src), amount * 4)
radiation_pulse(
src,
max_range = amount,
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE * 5,
minimum_exposure_time = 0,
strength = amount * 10
)
explosion(get_turf(src),round(amount / 12) , round(amount / 6), round(amount / 3), round(amount / 25))
qdel(src)
return
SSradiation.radiate(get_turf(src), amount * 2)
..()
+2 -2
View File
@@ -641,7 +641,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
gas_analyzing += "Temperature: [round(environment.temperature-T0C,0.1)]&deg;C ([round(environment.temperature,0.1)]K)"
gas_analyzing += "Heat Capacity: [round(environment.heat_capacity(),0.1)]"
to_chat(src, span_notice("[jointext(gas_analyzing, "<br>")]"))
/*
/mob/observer/dead/verb/check_radiation()
set name = "Check Radiation"
set category = "Ghost.Game"
@@ -650,7 +650,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(t)
var/rads = SSradiation.get_rads_at_turf(t)
to_chat(src, span_notice("Radiation level: [rads ? rads : "0"] Bq."))
*/
/mob/observer/dead/verb/view_manfiest()
set name = "Show Crew Manifest"
set category = "Ghost.Game"
+4 -2
View File
@@ -6,13 +6,13 @@
if(.)
return
if (radiation)
if (radiation > 100)
throw_alert("irradiated", /atom/movable/screen/alert/irradiated)
if(radiation > 100)
radiation = 100
if(!container)//If it's not in an MMI
to_chat(src, span_red("You feel weak."))
else//Fluff-wise, since the brain can't detect anything itself, the MMI handles thing like that
to_chat(src, span_red("STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED."))
switch(radiation)
if(1 to 49)
radiation--
@@ -35,6 +35,8 @@
radiation -= 3
adjustToxLoss(3)
updatehealth()
else
clear_alert("irradiated")
/mob/living/carbon/brain/handle_environment(datum/gas_mixture/environment)
@@ -251,7 +251,7 @@
/mob/living/carbon/human/inStasisNow()
var/stasisValue = getStasis()
if(stasisValue && (life_tick % stasisValue))
if(stasisValue && (life_tick % stasisValue) || HAS_TRAIT(src, TRAIT_STASIS))
return 1
return 0
@@ -610,3 +610,11 @@ emp_act
return TRUE
return FALSE
///Get all the clothing on a specific body part
/mob/living/carbon/human/proc/get_clothing_on_part(obj/item/organ/external/def_zone)
var/list/covering_part = list()
for(var/obj/item/clothing/equipped in get_equipped_items(INCLUDE_ABSTRACT))
if(equipped.body_parts_covered & def_zone.body_part)
covering_part += equipped
return covering_part
+24 -21
View File
@@ -47,37 +47,38 @@
voice = GetVoice()
var/stasis = inStasisNow()
var/stasis = (inStasisNow())
if(getStasis() > 2)
Sleeping(20)
//No need to update all of these procs if the guy is dead.
fall() //Prevents people from floating
if(stat != DEAD && !stasis)
//Updates the number of stored chemicals for powers
handle_changeling()
if(!stasis)
if(stat != DEAD)
//Updates the number of stored chemicals for powers
handle_changeling()
//Organs and blood
handle_organs()
stabilize_body_temperature() //Body temperature adjusts itself (self-regulation)
weightgain()
handle_shock()
//Organs and blood
handle_organs()
stabilize_body_temperature() //Body temperature adjusts itself (self-regulation)
weightgain()
handle_shock()
handle_pain()
handle_pain()
SEND_SIGNAL(src,COMSIG_HANDLE_ALLERGENS, chem_effects[CE_ALLERGEN])
SEND_SIGNAL(src,COMSIG_HANDLE_ALLERGENS, chem_effects[CE_ALLERGEN])
handle_medical_side_effects()
handle_medical_side_effects()
handle_heartbeat()
handle_nif()
if(phobias)
handle_phobias()
if(!client)
species.handle_npc(src)
handle_heartbeat()
handle_nif()
if(phobias)
handle_phobias()
if(!client)
species.handle_npc(src)
else if(stat == DEAD && !stasis)
handle_defib_timer()
else if(stat == DEAD)
handle_defib_timer()
//Handle any species related components we may have. Ex: Shadekin, Xenochimera, etc. Not STAT checked because those do statchecks in their own code.
handle_species_components()
@@ -262,11 +263,12 @@
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)
clear_alert("irradiated")
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(reagents.has_reagent(REAGENT_ID_PRUSSIANBLUE)) //Prussian Blue temporarily stops radiation effects.
if(HAS_TRAIT(src, TRAIT_HALT_RADIATION_EFFECTS)) //If we have a trait that halts radiation effects, then we just stop here. No need to do any of the checks below.
return
var/damage = 0
@@ -334,6 +336,7 @@
else if (radiation >= GLOB.radiation_levels[species.rad_levels]["danger_3"] && radiation < GLOB.radiation_levels[species.rad_levels]["danger_4"]) //Equivalent of 8.0 to 30 Gy.
throw_alert("irradiated", /atom/movable/screen/alert/irradiated)
damage = 10
radiation -= 100 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod
accumulated_rads += 100 * RADIATION_SPEED_COEFFICIENT
@@ -206,13 +206,6 @@
else
return ..()
/mob/living/simple_mob/slime/promethean/rad_act(severity)
rad_glow += severity
rad_glow = CLAMP(rad_glow,0,250)
if(rad_glow > 1)
set_light(max(1,min(5,rad_glow/15)), max(1,min(10,rad_glow/25)), color)
update_icon()
/mob/living/simple_mob/slime/promethean/bullet_act(obj/item/projectile/P)
if(humanform)
return humanform.bullet_act(P)
@@ -307,14 +307,6 @@
else
return ..()
/mob/living/simple_mob/protean_blob/rad_act(severity)
if(istype(loc, /obj/item/rig))
return //Don't irradiate us while we're in rig mode
if(humanform)
return humanform.rad_act(severity)
else
return ..()
/mob/living/simple_mob/protean_blob/bullet_act(obj/item/projectile/P)
if(humanform)
return humanform.bullet_act(P)
@@ -442,6 +442,14 @@
hidden = FALSE
activation_message="Your body feels mundane."
/datum/trait/positive/rad_immune/apply(var/datum/species/S,var/mob/living/carbon/human/H)
..()
ADD_TRAIT(H, TRAIT_RADIMMUNE, ROUNDSTART_TRAIT)
/datum/trait/positive/rad_immune/unapply(var/datum/species/S,var/mob/living/carbon/human/H)
..()
REMOVE_TRAIT(H, TRAIT_RADIMMUNE, ROUNDSTART_TRAIT)
/datum/trait/positive/vibration_sense
name = "Vibration Sense"
desc = "Allows you to sense subtle vibrations nearby, even if the source cannot be seen."
-1
View File
@@ -32,7 +32,6 @@
return name
/mob/living/Destroy()
SSradiation.listeners -= src
remove_all_modifiers(TRUE)
QDEL_NULL(say_list)
@@ -522,8 +522,14 @@
..()
/mob/living/simple_mob/slime/xenobio/green/proc/irradiate()
SSradiation.radiate(src, rads)
radiation_pulse(
src,
max_range = 5,
threshold = RAD_LIGHT_INSULATION,
chance = rads * 0.5,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = rads
)
/mob/living/simple_mob/slime/xenobio/pink
+15 -2
View File
@@ -578,7 +578,13 @@ GLOBAL_DATUM(planet_virgo3b, /datum/planet/virgo3b)
if(!T.is_outdoors())
return // They're indoors, so no need to irradiate them with fallout.
L.rad_act(rand(direct_rad_low, direct_rad_high))
radiation_pulse(
L,
max_range = 1,
threshold = RAD_VERY_LIGHT_INSULATION,
chance = rand(fallout_rad_low, fallout_rad_high),
strength = rand(fallout_rad_low, fallout_rad_high)
)
// This makes random tiles near people radioactive for awhile.
// Tiles far away from people are left alone, for performance.
@@ -590,7 +596,14 @@ GLOBAL_DATUM(planet_virgo3b, /datum/planet/virgo3b)
if(!istype(T))
return
if(T.is_outdoors())
SSradiation.radiate(T, rand(fallout_rad_low, fallout_rad_high))
radiation_pulse(
T,
max_range = 7,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = rand(fallout_rad_low, fallout_rad_high)
)
/datum/weather/virgo3b/fallout/temp
name = "short-term fallout"
+16 -2
View File
@@ -582,7 +582,14 @@ GLOBAL_DATUM(planet_virgo3c, /datum/planet/virgo3c)
if(!T.is_outdoors())
return // They're indoors, so no need to irradiate them with fallout.
L.rad_act(rand(direct_rad_low, direct_rad_high))
radiation_pulse(
L,
max_range = 1,
threshold = RAD_MEDIUM_INSULATION,
chance = rand(direct_rad_low, direct_rad_high),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = rand(direct_rad_low, direct_rad_high)
)
// This makes random tiles near people radioactive for awhile.
// Tiles far away from people are left alone, for performance.
@@ -594,7 +601,14 @@ GLOBAL_DATUM(planet_virgo3c, /datum/planet/virgo3c)
if(!istype(T))
return
if(T.is_outdoors())
SSradiation.radiate(T, rand(fallout_rad_low, fallout_rad_high))
radiation_pulse(
T,
max_range = 1,
threshold = RAD_MEDIUM_INSULATION,
chance = rand(direct_rad_low, direct_rad_high),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = rand(fallout_rad_low, fallout_rad_high)
)
/datum/weather/virgo3c/fallout/temp
name = "short-term fallout"
+17 -2
View File
@@ -553,7 +553,15 @@ GLOBAL_DATUM(planet_virgo4, /datum/planet/virgo4)
if(!T.is_outdoors())
return // They're indoors, so no need to irradiate them with fallout.
L.rad_act(rand(direct_rad_low, direct_rad_high))
radiation_pulse(
L,
max_range = 1,
threshold = RAD_MEDIUM_INSULATION,
chance = rand(direct_rad_low, direct_rad_high),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = rand(direct_rad_low, direct_rad_high)
)
// This makes random tiles near people radioactive for awhile.
// Tiles far away from people are left alone, for performance.
@@ -565,7 +573,14 @@ GLOBAL_DATUM(planet_virgo4, /datum/planet/virgo4)
if(!istype(T))
return
if(T.is_outdoors())
SSradiation.radiate(T, rand(fallout_rad_low, fallout_rad_high))
radiation_pulse(
T,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = rand(direct_rad_low, direct_rad_high),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = rand(fallout_rad_low, fallout_rad_high)
)
/datum/weather/virgo4/fallout/temp
name = "short-term fallout"
+14 -2
View File
@@ -333,7 +333,13 @@
radiation += plasma_temperature/2
plasma_temperature = 0
SSradiation.radiate(src, radiation)
radiation_pulse(
src,
max_range = 7,
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = energy * 0.001 //Might need to be increased.
)
Radiate()
/obj/effect/fusion_em_field/proc/Radiate()
@@ -539,7 +545,13 @@
//Reaction radiation is fairly buggy and there's at least three procs dealing with radiation here, this is to ensure constant radiation output.
/obj/effect/fusion_em_field/proc/radiation_scale()
SSradiation.radiate(src, 2 + plasma_temperature / PLASMA_TEMP_RADIATION_DIVISIOR)
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = (URANIUM_IRRADIATION_CHANCE + (plasma_temperature / PLASMA_TEMP_RADIATION_DIVISIOR)),
strength = energy * 0.01 //Might need to be increased.
)
//Somehow fixing the radiation issue managed to break this, but moving it to it's own proc seemed to have fixed it. I don't know.
/obj/effect/fusion_em_field/proc/temp_dump()
@@ -11,6 +11,34 @@
var/fuel_colour
var/radioactivity = 0
var/const/initial_amount = 3000000
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/fuel_assembly/process()
radiate()
/obj/item/fuel_assembly/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = (radioactivity * 0.5),
threshold = RAD_HEAVY_INSULATION,
chance = DEFAULT_RADIATION_CHANCE,
strength = radioactivity * 0.5
)
last_event = world.time
active = FALSE
/obj/item/fuel_assembly/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/fuel_assembly/Initialize(mapload, var/_material, var/_colour)
. = ..()
@@ -38,17 +66,6 @@
add_overlay(list(I, image(icon, "fuel_assembly_bracket")))
rod_quantities[fuel_type] = initial_amount
/obj/item/fuel_assembly/process()
if(!radioactivity)
return PROCESS_KILL
if(istype(loc, /turf))
SSradiation.radiate(src, max(1,CEILING(radioactivity/30, 1)))
/obj/item/fuel_assembly/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
// Mapper shorthand.
/obj/item/fuel_assembly/deuterium/Initialize(mapload)
. = ..(mapload, MAT_DEUTERIUM)
@@ -116,11 +116,13 @@ GLOBAL_LIST(fusion_reactions)
var/turf/origin = get_turf(holder)
holder.Rupture()
qdel(holder)
var/radiation_level = 200
// Copied from the SM for proof of concept. //Not any more --Cirra //Use the whole z proc --Leshana
SSradiation.z_radiate(locate(1, 1, holder.z), radiation_level, 1)
radiation_pulse(
holder,
max_range = 50,
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = 500
)
for(var/mob/living/mob in GLOB.living_mob_list)
var/turf/T = get_turf(mob)
@@ -135,8 +137,7 @@ GLOBAL_LIST(fusion_reactions)
spawn(5)
if(I && I.loc)
qdel(I)
sleep(5)
qdel(holder)
explosion(origin, 1, 2, 5)
return 1
+8 -1
View File
@@ -378,7 +378,14 @@ GLOBAL_LIST_EMPTY(gravity_generators)
/obj/machinery/gravity_generator/main/proc/pulse_radiation()
SSradiation.radiate(src, 200)
radiation_pulse(
src,
max_range = 7,
threshold = RAD_HEAVY_INSULATION,
chance = DEFAULT_RADIATION_CHANCE * 2,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = charge_count * 2
)
/obj/machinery/gravity_generator/main/proc/update_gravity(var/on)
for(var/area/A in src.areas)
+14 -2
View File
@@ -385,13 +385,25 @@
/obj/machinery/power/port_gen/pacman/super/UseFuel()
//produces a tiny amount of radiation when in use
if (prob(2*power_output))
SSradiation.radiate(src, 4)
radiation_pulse(
src,
max_range = 2,
threshold = RAD_HEAVY_INSULATION,
chance = DEFAULT_RADIATION_CHANCE,
strength = power_gen * 0.01
)
..()
/obj/machinery/power/port_gen/pacman/super/explode()
//a nice burst of radiation
var/rads = 50 + (sheets + sheet_left)*1.5
SSradiation.radiate(src, (max(20, rads)))
radiation_pulse(
src,
max_range = (rads/10),
threshold = RAD_HEAVY_INSULATION,
chance = DEFAULT_RADIATION_CHANCE,
strength = rads
)
explosion(src.loc, 3, 3, 5, 3)
qdel(src)
+15 -2
View File
@@ -144,7 +144,14 @@
..()
add_avail(power_gen)
if(panel_open && irradiate)
SSradiation.radiate(src, 60)
radiation_pulse(
src,
max_range = 3,
threshold = RAD_MEDIUM_INSULATION,
chance = DEFAULT_RADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = power_gen * 0.01 //1000 power = 10 rads. 10000 power = 100 rads. You can get creative with rad collectors if you want.
)
/obj/machinery/power/rtg/RefreshParts()
var/part_level = 0
@@ -593,9 +600,15 @@
var/turf/T = get_turf(src)
qdel(src)
if(T)
radiation_pulse(
T,
max_range = 50,
threshold = RAD_HEAVY_INSULATION,
chance = DEFAULT_RADIATION_CHANCE * 3,
strength = power_gen * 0.01 ///1MW = 1000 rads. If you blow up a BLACK HOLE ENGINE, you deserve the radiation that comes with it.
)
empulse(T, 12, 14, 16, 18)
explosion(T, 7, 12, 18, 20)
SSradiation.radiate(T, 200)
new /obj/effect/bhole(T)
/obj/machinery/power/rtg/antimatter_core/blob_act(obj/structure/blob/B)
+18 -12
View File
@@ -14,33 +14,36 @@
var/active = 0
var/locked = 0
var/drainratio = 1
rad_insulation = RAD_EXTREME_INSULATION //It sucks up the radiation. If you're standing behind it, you're pretty safe.
/obj/machinery/power/rad_collector/Initialize(mapload)
. = ..()
GLOB.rad_collectors += src
AddElement(/datum/element/climbable)
RegisterSignal(src, COMSIG_IN_RANGE_OF_IRRADIATION, PROC_REF(process_rads))
/obj/machinery/power/rad_collector/Destroy()
GLOB.rad_collectors -= src
UnregisterSignal(src, COMSIG_IN_RANGE_OF_IRRADIATION)
return ..()
/obj/machinery/power/rad_collector/process()
/obj/machinery/power/rad_collector/proc/process_rads(datum/source, datum/radiation_pulse_information/pulse_information)
SIGNAL_HANDLER
//so that we don't zero out the meter if the SM is processed first.
last_power = last_power_new
last_power_new = 0
if(P && active)
var/rads = SSradiation.get_rads_at_turf(get_turf(src))
if(rads)
receive_pulse(rads * 5) //Maths is hard
if(pulse_information)
var/amount_of_rads = pulse_information.strength
receive_pulse((amount_of_rads))
if(P)
if(P.air_contents.gas[GAS_PHORON] == 0)
investigate_log(span_red("out of fuel") + ".","singulo")
eject()
else
P.air_contents.adjust_gas(GAS_PHORON, -0.001*drainratio)
if(P.air_contents.gas[GAS_PHORON] == 0)
investigate_log(span_red("out of fuel") + ".","singulo")
eject()
else
P.air_contents.adjust_gas(GAS_PHORON, -0.0001*drainratio)
return
@@ -126,12 +129,15 @@
else
update_icons()
// Continuing here, SM giving us ~170 rads per pulse, a phoron canister full of 30 mols, and * 20 we get:
// 102000W per collector...So 10 collectors will give us ~1MW.
/obj/machinery/power/rad_collector/proc/receive_pulse(var/pulse_strength)
if(P && active)
var/power_produced = 0
power_produced = P.air_contents.gas[GAS_PHORON]*pulse_strength*20
add_avail(power_produced)
last_power_new = power_produced
if(power_produced)
add_avail(power_produced)
last_power_new = power_produced
return
return
@@ -148,13 +148,27 @@
/obj/machinery/particle_smasher/process()
if(!src.anchored) // Rapidly loses focus.
if(energy)
SSradiation.radiate(src, round(((src.energy-150)/50)*5,1))
radiation_pulse(
src,
max_range = 7,
threshold = RAD_HEAVY_INSULATION,
chance = round(((src.energy-150)/50)*5,1),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = energy * 0.1 //60 rads at max energy.
)
energy = max(0, energy - 30)
update_icon()
return
if(energy)
SSradiation.radiate(src, round(((src.energy-150)/50)*5,1))
radiation_pulse(
src,
max_range = 7,
threshold = RAD_HEAVY_INSULATION,
chance = round(((src.energy-150)/50)*5,1),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = energy * 0.1 //60 rads at max energy.
)
energy = CLAMP(energy - 5, 0, max_energy)
return
@@ -184,7 +198,14 @@
if(successful_craft)
visible_message(span_warning("\The [src] fizzles."))
if(prob(33)) // Why are you blasting it after it's already done!
SSradiation.radiate(src, 10 + round(src.energy / 60, 1))
radiation_pulse(
src,
max_range = 7,
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE + round(src.energy / 60, 1),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = energy * 0.1
)
energy = max(0, energy - 30)
update_icon()
return
+24 -4
View File
@@ -403,11 +403,16 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity)
/obj/singularity/proc/toxmob()
var/toxrange = 10
var/toxdamage = 4
var/radiation = 15
if (src.energy>200)
toxdamage = round(((src.energy-150)/50)*4,1)
radiation = round(((src.energy-150)/50)*5,1)
SSradiation.radiate(src, radiation) //Always radiate at max, so a decent dose of radiation is applied
radiation_pulse(
src,
max_range = 7,
threshold = RAD_EXTREME_INSULATION - 0.1,
chance = URANIUM_IRRADIATION_CHANCE + round(energy / 60, 1),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 250
)
for(var/mob/living/M in view(toxrange, src.loc))
if(SEND_SIGNAL(M, COMSIG_CHECK_FOR_GODMODE) & COMSIG_GODMODE_CANCEL)
return 0 // Cancelled by a component
@@ -450,13 +455,28 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity)
to_chat(M, span_danger("You hear an unearthly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat."))
to_chat(M, span_danger("You don't even have a moment to react as you are reduced to ashes by the intense radiation."))
M.dust()
SSradiation.radiate(src, rand(energy))
radiation_pulse(
src,
max_range = 10,
threshold = RAD_EXTREME_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE * 8,
strength = 1000
)
return
/obj/singularity/proc/pulse()
for(var/obj/machinery/power/rad_collector/R in GLOB.rad_collectors)
if (get_dist(R, src) <= 15) //Better than using orange() every process.
R.receive_pulse(energy)
//Yes, this means rad collectors can double dip on the singulo, but you could always use the safer SM or tesla, so it gets a small buff.
radiation_pulse(
src,
max_range = 15,
threshold = RAD_EXTREME_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE * 8,
strength = energy * 0.25
)
/obj/singularity/proc/on_capture()
chained = 1
+54 -12
View File
@@ -170,12 +170,27 @@
var/turf/TS = get_turf(src) // The turf supermatter is on. SM being in a locker, exosuit, or other container shouldn't block it's effects that way.
if(!istype(TS))
return
radiation_pulse(
TS,
max_range = 255,
threshold = RAD_HEAVY_INSULATION,
chance = DETONATION_RADS,
strength = DETONATION_RADS * 5
)
var/list/affected_z = GetConnectedZlevels(TS.z)
// Effect 1: Radiation, weakening to all mobs on Z level
for(var/z in affected_z)
SSradiation.z_radiate(locate(1, 1, z), DETONATION_RADS, 1)
for(var/z_to_affect in affected_z)
var/turf/turf_to_hit = locate(TS.x, TS.y, z_to_affect)
if(turf_to_hit)
radiation_pulse(
turf_to_hit,
max_range = 255,
threshold = RAD_HEAVY_INSULATION,
chance = DETONATION_RADS,
strength = DETONATION_RADS * 5
)
for(var/mob/living/mob in GLOB.living_mob_list)
var/turf/TM = get_turf(mob)
@@ -398,7 +413,16 @@
if(!istype(l.glasses, /obj/item/clothing/glasses/meson) || l.is_incorporeal()) // VOREStation Edit - Only mesons can protect you! OR if they're not in the same plane of existence
l.hallucination = max(0, min(200, l.hallucination + power * config_hallucination_power * sqrt( 1 / max(1,get_dist(l, src)) ) ) )
SSradiation.radiate(src, max(power * 1.5, 50) ) //Better close those shutters!
if(power)
// At a power mult of 0.025 for range, this means a 1000power SM (about normal) will reach 25 tiles and be putting off rad pulses of 500. With 0 protection, you have a 10% chance of getting hit.
radiation_pulse(
src,
max_range = CLAMP(max(round(power * 0.025), 3), 3, 50),
threshold = CLAMP(RAD_HEAVY_INSULATION - (power * 0.00025), 0.1, 1),
chance = round(power * 0.01),
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = round(power * 0.5)
)
power -= (power/DECAY_FACTOR)**3 //energy losses due to radiation
@@ -509,8 +533,13 @@
span_warning("The unearthly ringing subsides and you notice you have new radiation burns."), 2)
else
l.show_message(span_warning("You hear an uneartly ringing and notice your skin is covered in fresh radiation burns."), 2)
var/rads = 500
SSradiation.radiate(src, rads)
radiation_pulse(
src,
max_range = 5,
threshold = RAD_HEAVY_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = 200
)
/proc/supermatter_pull(var/atom/target, var/pull_range = 255, var/pull_power = STAGE_FIVE)
for(var/atom/A in range(pull_range, target))
@@ -547,18 +576,31 @@
desc = "The shattered remains of a supermatter shard plinth. It doesn't look safe to be around."
icon = 'icons/obj/supermatter.dmi'
icon_state = "darkmatter_broken"
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/broken_sm/Initialize(mapload)
. = ..()
message_admins("Broken SM shard created at ([x],[y],[z] - <A href='byond://?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
START_PROCESSING(SSobj, src)
/obj/item/broken_sm/process()
SSradiation.radiate(src, 50)
/obj/item/broken_sm/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/broken_sm/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 25
)
last_event = world.time
active = FALSE
#undef POWER_FACTOR
#undef DECAY_FACTOR
+8 -2
View File
@@ -166,8 +166,14 @@
icon_scale_y = 2
var/rad_power = 50
/obj/item/projectile/arc/radioactive/on_impact(turf/T)
SSradiation.radiate(T, rad_power)
/obj/item/projectile/arc/radioactive/on_impact(turf/T) //This means you can shoot a rad collector to generate power...Might need to be adjusted.
radiation_pulse(
T,
max_range = 3,
threshold = RAD_LIGHT_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
strength = rad_power
)
// Blob mortar
/obj/item/projectile/arc/spore
-73
View File
@@ -1,73 +0,0 @@
// Describes a point source of radiation. Created either in response to a pulse of radiation, or over an irradiated atom.
// Sources will decay over time, unless something is renewing their power!
/datum/radiation_source
var/turf/source_turf // Location of the radiation source.
var/rad_power // Strength of the radiation being emitted.
var/decay = TRUE // True for automatic decay. False if owner promises to handle it (i.e. supermatter)
var/respect_maint = FALSE // True for not affecting RAD_SHIELDED areas.
var/flat = FALSE // True for power falloff with distance.
var/range // Cached maximum range, used for quick checks against mobs.
/datum/radiation_source/Destroy()
SSradiation.sources -= src
if(SSradiation.sources_assoc[src.source_turf] == src)
SSradiation.sources_assoc -= src.source_turf
src.source_turf = null
. = ..()
/datum/radiation_source/proc/update_rad_power(var/new_power = null)
if(new_power == null || new_power == rad_power)
return // No change
else if(new_power <= CONFIG_GET(number/radiation_lower_limit))
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
/turf
var/cached_rad_resistance = 0
/turf/proc/calc_rad_resistance()
cached_rad_resistance = 0
for(var/obj/O in src.contents)
if(O.rad_resistance) //Override
cached_rad_resistance += O.rad_resistance
else if(O.density) //So open doors don't get counted
var/datum/material/M = O.get_material()
if(!M) continue
cached_rad_resistance += (M.weight + M.radiation_resistance) / CONFIG_GET(number/radiation_material_resistance_divisor)
// Looks like storing the contents length is meant to be a basic check if the cache is stale due to items enter/exiting. Better than nothing so I'm leaving it as is. ~Leshana
SSradiation.resistance_cache[src] = (length(contents) + 1)
return
/turf/simulated/wall/calc_rad_resistance()
SSradiation.resistance_cache[src] = (length(contents) + 1)
var/temp_rad_resistance
temp_rad_resistance += material.weight + material.radiation_resistance
if(reinf_material)
temp_rad_resistance += reinf_material.weight + reinf_material.radiation_resistance
cached_rad_resistance = (density ? (temp_rad_resistance) / CONFIG_GET(number/radiation_material_resistance_divisor) : 0)
return
/turf/simulated/mineral/calc_rad_resistance()
if(!density)
return ..()
SSradiation.resistance_cache[src] = (length(contents) + 1)
cached_rad_resistance = 60 //Three times that of a steel wall. Rock is less dense than steel, but this is assuming that a normal wall isn't just solid steel all the way through like rock turfs are.
return
// If people expand the system, this may be useful. Here as a placeholder until then
/atom/proc/rad_act(var/severity)
return 1
/mob/living/rad_act(var/severity)
if(severity && !isbelly(loc)) //eaten mobs are made immune to radiation //VOREStation Edit
src.apply_effect(severity, IRRADIATE, src.getarmor(null, "rad"))
for(var/atom/I in src)
I.rad_act(severity)
return
+2
View File
@@ -70,6 +70,8 @@
total_volume = 0
for(var/datum/reagent/R in reagent_list)
if(R.volume < MINIMUM_CHEMICAL_VOLUME)
if(isliving(R.holder.my_atom))
R.on_mob_end_metabolize(R.holder.my_atom, src)
del_reagent(R.id)
else
total_volume += R.volume
@@ -46,6 +46,9 @@
var/wiki_flag = 0 // Bitflags for secret/food/drink reagent sorting
var/supply_conversion_value = null
var/industrial_use = null // unique description for export off station
/// A list of traits to apply while the reagent is being metabolized.
var/list/metabolized_traits
var/coolant_modifier = -0.5 // this is multiplied by the volume of the reagent. Most things are not good coolant. EX: Water is 1, coolant is 2. -1 would be a bad reagent for cooling.
@@ -186,6 +189,7 @@
affect_ingest(M, alien, removed * ingest_abs_mult)
if(CHEM_TOUCH)
affect_touch(M, alien, removed)
on_mob_metabolize(M, location)
if(overdose && (volume > overdose * M?.species.chemOD_threshold) && (active_metab.metabolism_class != CHEM_TOUCH || can_overdose_touch))
overdose(M, alien, removed)
if(M.species.allergens & allergen_type) //uhoh, we can't handle this!
@@ -241,3 +245,14 @@
/// Called by [/datum/reagents/proc/conditional_update]
/datum/reagent/proc/on_update(atom/A)
return
/datum/reagent/proc/on_mob_metabolize(mob/living/affected_mob, datum/reagents/metabolism/location)
SHOULD_CALL_PARENT(TRUE)
if(metabolized_traits)
affected_mob.add_traits(metabolized_traits, "metabolize_location:[location]reagent:[type]")
/// Called when this reagent stops being metabolized (due to running out)
/// Has the args 'affected_mob' and 'location' which allows us to remove any traits that is only being added by that reagent holder location. I.e stomach, bloodstream, dermal, etc.
/datum/reagent/proc/on_mob_end_metabolize(mob/living/affected_mob, datum/reagents/location)
SHOULD_CALL_PARENT(TRUE)
REMOVE_TRAITS_IN(affected_mob, "metabolize_location:[location]reagent:[type]")
@@ -4132,6 +4132,19 @@
allergen_type = ALLERGEN_FRUIT|ALLERGEN_GRAINS //Made from vodka(grains) and orange juice(fruit)
/datum/reagent/ethanol/screwdrivercocktail/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, metabolization_ratio)
. = ..()
// var/obj/item/organ/internal/liver/liver = drinker.internal_organs_by_name[O_LIVER]
// if(HAS_TRAIT(liver, TRAIT_ENGINEER_METABOLISM))
ADD_TRAIT(drinker, TRAIT_HALT_RADIATION_EFFECTS, "[type]")
if (HAS_TRAIT(drinker, TRAIT_IRRADIATED))
if(drinker.adjustToxLoss(-2 * metabolization_ratio * seconds_per_tick))
return //UPDATE_MOB_HEALTH
/datum/reagent/ethanol/screwdrivercocktail/on_mob_end_metabolize(mob/living/drinker)
. = ..()
REMOVE_TRAIT(drinker, TRAIT_HALT_RADIATION_EFFECTS, "[type]")
/datum/reagent/ethanol/silencer
name = REAGENT_SILENCER
id = REAGENT_ID_SILENCER
@@ -118,6 +118,7 @@
scannable = SCANNABLE_BENEFICIAL
supply_conversion_value = REFINERYEXPORT_VALUE_COMMON
industrial_use = REFINERYEXPORT_REASON_DRUG
metabolized_traits = list(TRAIT_HALT_RADIATION_EFFECTS)
/datum/reagent/prussian_blue/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
if(alien == IS_DIONA)
+1 -1
View File
@@ -1098,7 +1098,7 @@
industrial_use = REFINERYEXPORT_REASON_BIOHAZARD
/datum/reagent/irradiated_nanites/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
SSradiation.radiate(get_turf(M), 20) // Irradiate people around you.
//SSradiation.radiate(get_turf(M), 20) // Irradiate people around you. //TODO
M.radiation = max(M.radiation + 5 * removed, 0) // Irradiate you. Because it's inside you.
/datum/reagent/neurophage_nanites
@@ -24,7 +24,7 @@
// Station Suits
/datum/design_techweb/mechfab/modsuit/eva_controller
name = "EVA Suit Control Module"
desc = "An engineering Hardsuit featuring a visor with welding protection, iommunity to radiation, and insulated gauntlets. It is well insulated against the heat."
desc = "An engineering Hardsuit featuring a visor with welding protection, immunity to radiation, and insulated gauntlets. It is well insulated against the heat."
id = "eva_rig_module"
materials = list(MAT_PLASTEEL = 16000, MAT_GOLD = 3000, MAT_GRAPHITE = 4500, MAT_OSMIUM = 1000, MAT_PLASTIC = 4500, MAT_LEAD = 2000, MAT_STEEL = 2000)
build_path = /obj/item/rig/eva
@@ -21,7 +21,14 @@
if(istype(holder, /obj/item/anobattery))
holder = holder.loc
if(holder)
SSradiation.flat_radiate(holder, radiation_amount, src.effectrange)
radiation_pulse(
holder,
max_range = effectrange,
threshold = RAD_LIGHT_INSULATION,
chance = chargelevelmax * 0.5,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = chargelevelmax * 0.5
)
return 1
/datum/artifact_effect/radiate/DoEffectPulse()
@@ -29,5 +36,11 @@
if(istype(holder, /obj/item/anobattery))
holder = holder.loc
if(holder)
SSradiation.radiate(holder, ((radiation_amount * 3) * (sqrt(src.effectrange))))
radiation_pulse(
holder,
max_range = effectrange,
threshold = RAD_LIGHT_INSULATION,
chance = chargelevelmax,
strength = chargelevelmax * 2,
)
return 1
+31 -2
View File
@@ -910,7 +910,35 @@
description_info = "When injected with phoron, this extract creates a single radioactive pulse. When injected with blood, this extract creates a radioactive glob. When injected with water \
this extract creates some radium. When injected with slime jelly, this extract creates some uranium."
slime_type = /mob/living/simple_mob/slime/xenobio/green
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/slime_extract/green/process()
radiate()
..()
/obj/item/slime_extract/green/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 50
)
last_event = world.time
active = FALSE
/obj/item/slime_extract/green/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/datum/decl/chemical_reaction/instant/slime/green_radpulse
name = "Slime Radiation Pulse"
@@ -924,8 +952,9 @@
playsound(holder.my_atom, 'sound/effects/phasein.ogg', 75, 1)
holder.my_atom.visible_message(span_danger("\The [holder.my_atom] begins to vibrate violently!"))
spawn(5 SECONDS)
SSradiation.flat_radiate(src, 30, 7, TRUE)
..()
if(!QDELETED(holder.my_atom) && istype(holder.my_atom, /obj/item/slime_extract/green))
START_PROCESSING(SSobj, src)
/datum/decl/chemical_reaction/instant/slime/green_emitter
+22 -1
View File
@@ -168,6 +168,9 @@
light_power = 0.4
light_range = 2
w_class = ITEMSIZE_TINY
var/last_event = 0
/// Mutex to prevent infinite recursion when propagating radiation pulses
var/active = null
/obj/item/slime_irradiator/Initialize(mapload)
. = ..()
@@ -175,7 +178,25 @@
set_light(light_range, light_power, light_color)
/obj/item/slime_irradiator/process()
SSradiation.radiate(src, 5)
radiate()
/obj/item/slime_irradiator/proc/radiate()
SIGNAL_HANDLER
if(active)
return
if(world.time <= last_event + 1.5 SECONDS)
return
active = TRUE
radiation_pulse(
src,
max_range = 5,
threshold = RAD_MEDIUM_INSULATION,
chance = URANIUM_IRRADIATION_CHANCE,
minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,
strength = 25
)
last_event = world.time
active = FALSE
/obj/item/slime_irradiator/Destroy()
STOP_PROCESSING(SSobj, src)