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