diff --git a/code/controllers/Processes/radiation.dm b/code/controllers/Processes/radiation.dm
index 8ebd3a71a8e..e64f71ebb7d 100644
--- a/code/controllers/Processes/radiation.dm
+++ b/code/controllers/Processes/radiation.dm
@@ -7,37 +7,43 @@
linked = radiation_repository
/datum/controller/process/radiation/doWork()
-// set background = 1
- for(var/turf/T in linked.irradiated_turfs)
- if(!T)
- linked.irradiated_turfs.Remove(T)
+ // Step 1 - Sources Decay
+ var/list/sources = linked.sources
+ for(var/thing in sources)
+ if(deleted(thing))
+ sources.Remove(thing)
continue
- linked.irradiated_turfs[T] -= config.radiation_decay_rate
- if(linked.irradiated_turfs[T] <= config.radiation_lower_limit)
- linked.irradiated_turfs.Remove(T)
- SCHECK
- for(var/mob/living/L in linked.irradiated_mobs)
- if(!L)
- linked.irradiated_mobs.Remove(L)
- continue
- if(get_turf(L) in linked.irradiated_turfs)
- L.rad_act(linked.irradiated_turfs[get_turf(L)])
- if(!L.radiation)
- linked.irradiated_mobs.Remove(L)
- SCHECK
- for(var/thing in linked.sources)
- if(!thing)
- linked.sources.Remove(thing)
- continue
- var/atom/emitter = thing
- linked.radiate(emitter, emitter.rad_power)
- to_process.Cut()
- SCHECK
- for(var/thing in linked.resistance_cache)
- if(!thing)
- linked.resistance_cache.Remove(thing)
+ var/datum/radiation_source/S = thing
+ if(S.decay)
+ S.update_rad_power(S.rad_power - config.radiation_decay_rate)
+ if(S.rad_power <= config.radiation_lower_limit)
+ sources.Remove(S)
+ SCHECK // This scheck probably just wastes resources, but better safe than sorry in this case.
+
+ // Step 2 - Cache Expires
+ var/list/resistance_cache = linked.resistance_cache
+ for(var/thing in resistance_cache)
+ if(deleted(thing))
+ resistance_cache.Remove(thing)
continue
var/turf/T = thing
- if((length(T.contents) + 1) != linked.resistance_cache[T])
- T.calc_rad_resistance()
- SCHECK
\ No newline at end of file
+ if((length(T.contents) + 1) != resistance_cache[T])
+ resistance_cache.Remove(T) // If its stale REMOVE it! It will get added if its needed.
+ SCHECK
+
+ // Step 3 - Registered irradiatable things are checked for radiation
+ var/list/registered_listeners = living_mob_list // For now just use this. Nothing else is interested anyway.
+ if(length(linked.sources) > 0)
+ for(var/thing in registered_listeners)
+ if(deleted(thing))
+ continue
+ var/atom/A = thing
+ var/turf/T = get_turf(thing)
+ var/rads = linked.get_rads_at_turf(T)
+ if(rads)
+ A.rad_act(rads)
+ SCHECK
+
+/datum/controller/process/radiation/statProcess()
+ ..()
+ stat(null, "[linked.sources.len] sources, [linked.resistance_cache.len] cached turfs")
diff --git a/code/datums/repositories/radiation.dm b/code/datums/repositories/radiation.dm
index bdf2b183226..457e423b4eb 100644
--- a/code/datums/repositories/radiation.dm
+++ b/code/datums/repositories/radiation.dm
@@ -1,114 +1,132 @@
-//#define RADDBG
-
-var/repository/radiation/radiation_repository = new()
-
-var/list/to_process = list()
+var/global/repository/radiation/radiation_repository = new()
/repository/radiation
- var/list/sources = list() //All the radiation sources we know about
- var/list/irradiated_turfs = list()
- var/list/irradiated_mobs = list()
- var/list/resistance_cache = list()
+ 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.
-/repository/radiation/proc/report_rads(var/turf/T as turf)
- return irradiated_turfs[T]
+// 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()
+ radiation_repository.sources -= src
+ if(radiation_repository.sources_assoc[src.source_turf] == src)
+ radiation_repository.sources -= src.source_turf
+ . = ..()
+
+/datum/radiation_source/proc/update_rad_power(var/new_power = null)
+ if(new_power != null && new_power != rad_power)
+ rad_power = new_power
+ . = 1
+ if(. && !flat)
+ range = min(round(sqrt(rad_power / config.radiation_lower_limit)), 31)
+
+// Ray trace from all active radiation sources to T and return the strongest effect.
+/repository/radiation/proc/get_rads_at_turf(var/turf/T)
+ if(!istype(T)) return 0
+
+ . = 0
+ for(var/value in sources)
+ var/datum/radiation_source/source = value
+ if(source.rad_power < .)
+ continue // Already being affected by a stronger source
+ var/dist = get_dist(source.source_turf, T)
+ if(dist > source.range)
+ continue // Too far to possibly affect
+ if(source.respect_maint)
+ var/atom/A = T.loc
+ if(A.flags & RAD_SHIELDED)
+ continue // In shielded area
+ if(source.flat)
+ . = max(., source.rad_power)
+ continue // No need to ray trace for flat field
+
+ // 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(!(origin in resistance_cache)) //Only get the resistance if we don't already know it.
+ origin.calc_rad_resistance()
+ working = max((working - (origin.cached_rad_resistance * config.radiation_resistance_multiplier)), 0)
+ if(working <= .)
+ break // Already affected by a stronger source (or its zero...)
+ . = max((working * (1 / (dist ** 2))), .) //Butchered version of the inverse square law. Works for this purpose
+
+// Add a radiation source instance to the repository. It will override any existing source on the same turf.
+/repository/radiation/proc/add_source(var/datum/radiation_source/S)
+ 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
/repository/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)
+ return S
- var/range = min(round(sqrt(power / config.radiation_lower_limit)), 31)
- var/turf/epicentre = get_turf(source)
- to_process = list()
-
- range = min(epicentre.x, world.maxx - epicentre.x, epicentre.y, world.maxy - epicentre.y, range)
-
- to_process = trange(range, epicentre)
- to_process[epicentre] = power
-
- for(var/turf/spot in to_process)
- var/turf/origin = get_turf(epicentre)
- var/working = power
- while(origin != spot)
- origin = get_step_towards(origin, spot) //Raytracing
- if(!(origin in resistance_cache)) //Only get the resistance if we don't already know it.
- origin.calc_rad_resistance()
- working = max((working - (origin.rad_resistance * config.radiation_resistance_multiplier)), 0)
- if(!working)
- break
- if(!to_process[origin])
- to_process[origin] = working
-
- else
- to_process[origin] = max(to_process[origin], working)
-
- for(var/turf/spot in to_process)
- irradiated_turfs[spot] = max(((to_process[spot]) * (1 / (get_dist(epicentre, spot) ** 2))), irradiated_turfs[spot]) //Butchered version of the inverse square law. Works for this purpose
- #ifdef RADDBG
- var/x = Clamp( irradiated_turfs[spot], 0, 255)
- spot.color = rgb(5,x,5)
- #endif
-
-/repository/radiation/proc/flat_radiate(source, power, range, var/respect_maint=0) //Sets the radiation in a range to a constant value.
+// Sets the radiation in a range to a constant value.
+/repository/radiation/proc/flat_radiate(source, power, range, var/respect_maint = FALSE)
if(!(source && power && range))
return
- var/turf/epicentre = get_turf(source)
- range = min(epicentre.x, world.maxx - epicentre.x, epicentre.y, world.maxy - epicentre.y, range)
- if(!respect_maint)
- for(var/turf/T in trange(range, epicentre))
- irradiated_turfs[T] = max(power, irradiated_turfs[T])
- else
- for(var/turf/T in trange(range, epicentre))
- var/area/A = T.loc
- if(A.flags & RAD_SHIELDED)
- continue
- irradiated_turfs[T] = max(power, irradiated_turfs[T])
+ 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)
+ return S
-/repository/radiation/proc/z_radiate(var/atom/source, power, var/respect_maint=0) //Irradiates a full Z-level. Hacky way of doing it, but not too expensive.
+// Irradiates a full Z-level. Hacky way of doing it, but not too expensive.
+/repository/radiation/proc/z_radiate(var/atom/source, power, var/respect_maint = FALSE)
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)
+/turf
+ var/cached_rad_resistance = 0
+
/turf/proc/calc_rad_resistance()
- rad_resistance = 0
+ cached_rad_resistance = 0
for(var/obj/O in src.contents)
if(O.rad_resistance) //Override
- rad_resistance += O.rad_resistance
+ cached_rad_resistance += O.rad_resistance
- else if(O.density) //So doors don't get counted
+ else if(O.density) //So open doors don't get counted
var/material/M = O.get_material()
if(!M) continue
- rad_resistance += M.weight
+ cached_rad_resistance += M.weight
+ // 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
radiation_repository.resistance_cache[src] = (length(contents) + 1)
/turf/simulated/wall/calc_rad_resistance()
radiation_repository.resistance_cache[src] = (length(contents) + 1)
- rad_resistance = (density ? material.weight : 0)
+ cached_rad_resistance = (density ? material.weight : 0)
-/atom
- var/rad_power = 0
- var/rad_resistance = 0
+/obj
+ var/rad_resistance = 0 // Allow overriding rad resistance
-/atom/Destroy()
- if(rad_power)
- radiation_repository.sources.Remove(src)
- . = ..()
-
-/atom/proc/update_radiation() //For VV'ing something to make it radioactive at runtime
- if((rad_power) && (!(src in radiation_repository.sources)))
- radiation_repository.sources.Add(src)
- else if((!rad_power) && (src in radiation_repository.sources))
- radiation_repository.sources.Remove(src)
-
-/atom/proc/rad_act(var/severity) //If people expand the system, this may be useful. Here as a placeholder until then
+// 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)
src.apply_effect(severity, IRRADIATE, src.getarmor(null, "rad"))
- for(var/obj/I in src)
+ for(var/atom/I in src)
I.rad_act(severity)
-
-
-//#undef RADDBG
\ No newline at end of file
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 82061ddb6ad..fe9f99b0328 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -53,9 +53,6 @@
pulledby = null
/atom/movable/proc/initialize()
- if(rad_power)
- radiation_repository.sources.Add(src)
-
if(!isnull(gcDestroyed))
crash_with("GC: -- [type] had initialize() called after qdel() --")
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index c1ec9287b0e..5d8d068d49d 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -263,7 +263,7 @@
icon = 'icons/obj/doors/Dooruranium.dmi'
mineral = "uranium"
var/last_event = 0
- rad_power = 7.5
+ var/rad_power = 7.5
/obj/machinery/door/airlock/process()
// Deliberate no call to parent.
@@ -278,6 +278,13 @@
..()
+/obj/machinery/door/airlock/uranium/process()
+ if(world.time > last_event+20)
+ if(prob(50))
+ radiation_repository.radiate(src, rad_power)
+ last_event = world.time
+ ..()
+
/obj/machinery/door/airlock/phoron
name = "Phoron Airlock"
desc = "No way this can end badly."
diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm
index f8e1d1cbb59..41680d3de76 100644
--- a/code/game/machinery/doors/blast_door.dm
+++ b/code/game/machinery/doors/blast_door.dm
@@ -53,8 +53,7 @@
icon_state = icon_state_closed
else
icon_state = icon_state_open
- var/turf/T = get_turf(src)
- T.calc_rad_resistance()
+ radiation_repository.resistance_cache.Remove(get_turf(src))
return
// Proc: force_open()
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 898683192ff..cf7f96d52da 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -362,8 +362,7 @@
icon_state = "door1"
else
icon_state = "door0"
- var/turf/T = get_turf(src)
- T.calc_rad_resistance()
+ radiation_repository.resistance_cache.Remove(get_turf(src))
return
diff --git a/code/game/objects/items/devices/geiger.dm b/code/game/objects/items/devices/geiger.dm
index acd85b730c5..62cb9d9862c 100644
--- a/code/game/objects/items/devices/geiger.dm
+++ b/code/game/objects/items/devices/geiger.dm
@@ -22,7 +22,7 @@
/obj/item/device/geiger/process()
if(!scanning)
return
- radiation_count = radiation_repository.report_rads(get_turf(src))
+ radiation_count = radiation_repository.get_rads_at_turf(get_turf(src))
update_icon()
/obj/item/device/geiger/examine(mob/user)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 3d48a403749..cfd0693b15d 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -69,7 +69,7 @@ REAGENT SCANNER
user.show_message("Analyzing Results for [M]:")
user.show_message("Overall Status: dead")
else
- user.show_message("Analyzing Results for [M]:\n\t Overall Status: [M.stat > 1 ? "dead" : "[round(M.health*100/M.getMaxHealth())]% healthy"]")
+ user.show_message("Analyzing Results for [M]:\n\t Overall Status: [M.stat > 1 ? "dead" : "[round((M.health/M.getMaxHealth())*100) ]% healthy"]")
user.show_message(" Key: Suffocation/Toxin/Burns/Brute", 1)
user.show_message(" Damage Specifics: [OX] - [TX] - [BU] - [BR]")
user.show_message("Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)", 1)
@@ -100,9 +100,7 @@ REAGENT SCANNER
OX = fake_oxy > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal"
user.show_message("[OX] | [TX] | [BU] | [BR]")
if(M.radiation)
- user.show_message("Radiation Level: [M.radiation]")
- else
- user.show_message("No radiation detected.")
+ user.show_message("Radiation detected.")
if(istype(M, /mob/living/carbon))
var/mob/living/carbon/C = M
if(C.reagents.total_volume)
diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm
index b236e4dc904..890574d6641 100644
--- a/code/game/turfs/simulated/wall_attacks.dm
+++ b/code/game/turfs/simulated/wall_attacks.dm
@@ -7,7 +7,7 @@
if(can_open == WALL_OPENING)
return
- calc_rad_resistance()
+ radiation_repository.resistance_cache.Remove(src)
if(density)
can_open = WALL_OPENING
diff --git a/code/game/turfs/simulated/wall_icon.dm b/code/game/turfs/simulated/wall_icon.dm
index 757639e1bba..eab99bfba72 100644
--- a/code/game/turfs/simulated/wall_icon.dm
+++ b/code/game/turfs/simulated/wall_icon.dm
@@ -26,7 +26,7 @@
else if(material.opacity < 0.5 && opacity)
set_light(0)
- calc_rad_resistance()
+ radiation_repository.resistance_cache.Remove(src)
update_connections(1)
update_icon()
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 87b4b2b6486..3f2e0064f22 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -140,12 +140,6 @@ var/const/enterloopsanity = 100
if(ismob(A))
var/mob/M = A
- var/mob/living/L = A
- if(istype(L))
- if(!(L in radiation_repository.irradiated_mobs))
- if(src in radiation_repository.irradiated_turfs)
- radiation_repository.irradiated_mobs.Add(L)
- L.handle_footstep(src)
if(!M.lastarea)
M.lastarea = get_area(M.loc)
if(M.lastarea.has_gravity == 0)
@@ -153,6 +147,9 @@ var/const/enterloopsanity = 100
else if(!is_space())
M.inertia_dir = 0
M.make_floating(0)
+ if(isliving(M))
+ var/mob/living/L = M
+ L.handle_footstep(src)
..()
var/objects = 0
if(A && (A.flags & PROXMOVE))
@@ -258,4 +255,4 @@ var/const/enterloopsanity = 100
step(AM, turn(AM.last_move, 180))
if(isliving(AM))
var/mob/living/M = AM
- M.turf_collision(src, speed)
\ No newline at end of file
+ M.turf_collision(src, speed)
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 804d13404dc..37a576a4420 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -186,4 +186,4 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
#undef ASSIGNMENT_JANITOR
#undef ASSIGNMENT_MEDICAL
#undef ASSIGNMENT_SCIENTIST
-#undef ASSIGNMENT_SECURITY
\ No newline at end of file
+#undef ASSIGNMENT_SECURITY
diff --git a/code/modules/events/solar_storm.dm b/code/modules/events/solar_storm.dm
index 4ebfc71d555..afaa01ed95a 100644
--- a/code/modules/events/solar_storm.dm
+++ b/code/modules/events/solar_storm.dm
@@ -27,14 +27,17 @@
radiate()
/datum/event/solar_storm/proc/radiate()
- var/radiation_level = rand(15, 30)
- for(var/area/A in all_areas)
- if(!(A.z in using_map.player_levels))
+ // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case.
+ for(var/mob/living/L in living_mob_list)
+ var/turf/T = get_turf(L)
+ if(!T)
continue
- for(var/turf/T in A)
- if(!istype(T.loc,/area/space) && !istype(T,/turf/space))
- continue
- radiation_repository.irradiated_turfs[T] = radiation_level
+
+ if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf
+ continue
+
+ //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation.
+ L.rad_act(rand(15, 30))
/datum/event/solar_storm/end()
command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. Please report to medbay if you experience any unusual symptoms. ", "Anomaly Alert")
diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm
index 68363fb27fa..a6a857c43b8 100644
--- a/code/modules/mining/mine_turfs.dm
+++ b/code/modules/mining/mine_turfs.dm
@@ -559,4 +559,4 @@ var/list/mining_overlay_cache = list()
if(mineral_name && (mineral_name in ore_data))
mineral = ore_data[mineral_name]
UpdateMineral()
- update_icon()
\ No newline at end of file
+ update_icon()
diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm
index ba817d392a0..47a9c08923e 100644
--- a/code/modules/power/singularity/collector.dm
+++ b/code/modules/power/singularity/collector.dm
@@ -31,10 +31,10 @@ var/global/list/rad_collectors = list()
last_power_new = 0
- var/turf/T = get_turf(src)
- if(T in radiation_repository.irradiated_turfs)
- receive_pulse((radiation_repository.irradiated_turfs[T] * 5)) //Maths is hard
-
+ if(P && active)
+ var/rads = radiation_repository.get_rads_at_turf(get_turf(src))
+ if(rads)
+ receive_pulse(rads * 5) //Maths is hard
if(P)
if(P.air_contents.gas["phoron"] == 0)
diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm
index 9db6eca0ad0..046c315ec98 100644
--- a/code/modules/supermatter/supermatter.dm
+++ b/code/modules/supermatter/supermatter.dm
@@ -42,7 +42,6 @@
anchored = 0
light_range = 4
- rad_power = 1 //So it gets added to the repository
var/gasefficency = 0.25
var/base_icon_state = "darkmatter"
@@ -112,7 +111,7 @@
var/mob/living/carbon/human/H = mob
H.hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(mob, src) + 1)) ) )
spawn(pull_time)
- explosion(TS, explosion_power, explosion_power * 2, explosion_power * 3, explosion_power * 4, 1)
+ explosion(get_turf(src), explosion_power, explosion_power * 2, explosion_power * 3, explosion_power * 4, 1)
qdel(src)
return
@@ -273,7 +272,7 @@
var/rads = (power / 10) * ( 1 / (radius**2) )
l.apply_effect(rads, IRRADIATE)
*/
- rad_power = power * 1.5//Better close those shutters!
+ radiation_repository.radiate(src, power * 1.5) //Better close those shutters!
power -= (power/DECAY_FACTOR)**3 //energy losses due to radiation
@@ -403,7 +402,6 @@
return
-
/obj/machinery/power/supermatter/GotoAirflowDest(n) //Supermatter not pushed around by airflow
return