diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index c585a822ea4c..4823bdebc203 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -352,3 +352,6 @@ GLOBAL_LIST_INIT(pipe_paint_colors, list(
"violet" = rgb(64,0,128),
"yellow" = rgb(255,198,0)
))
+
+#define AIR_REF_PLANETARY_TURF (1<<0) //SIMULATION_DIFFUSE 0b1
+#define AIR_REF_OPEN_TURF (1<<1) //SIMULATION_ALL 0b10
diff --git a/code/__SANDCODE/DEFINES/chat.dm b/code/__SANDCODE/DEFINES/chat.dm
index 79da5c479d6f..8a8cc84eefe7 100644
--- a/code/__SANDCODE/DEFINES/chat.dm
+++ b/code/__SANDCODE/DEFINES/chat.dm
@@ -1,2 +1,4 @@
/// Adds a generic box around whatever message you're sending in chat. Really makes things stand out.
#define examine_block(str) ("
" + str + "
")
+
+#define MESSAGE_TYPE_INFO "info"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 0a659bc48f8e..9180be964980 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1590,3 +1590,7 @@
else
//We inline a MAPTEXT() here, because there's no good way to statically add to a string like this
active_hud.screentip_text.maptext = "[name][extra_context]"
+
+///Return the air if we can analyze it
+/atom/proc/return_analyzable_air()
+ return null
diff --git a/code/game/gamemodes/clown_ops/clown_weapons.dm b/code/game/gamemodes/clown_ops/clown_weapons.dm
index 5891facfb70b..5224cc306e9e 100644
--- a/code/game/gamemodes/clown_ops/clown_weapons.dm
+++ b/code/game/gamemodes/clown_ops/clown_weapons.dm
@@ -137,7 +137,7 @@
..()
slipper.signal_enabled = active
-/obj/item/shield/energy/bananium/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/shield/energy/bananium/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
if(active)
if(iscarbon(thrower))
var/mob/living/carbon/C = thrower
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 4ca642843bf9..78b7de48fc6f 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -738,7 +738,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
-/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
+/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE, quickstart = TRUE)
thrownby = WEAKREF(thrower)
callback = CALLBACK(src, PROC_REF(after_throw), callback, (spin && messy_throw)) //replace their callback with our own
. = ..(target, range, speed, thrower, spin, diagonals_first, callback, force)
@@ -1077,7 +1077,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
dropped(M)
return ..()
-/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback)
+/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback, quickstart = TRUE)
if (HAS_TRAIT(src, TRAIT_NODROP))
return
return ..()
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 428e9cf67bac..0255afeeba10 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -725,16 +725,21 @@ GENETICS SCANNER
amount += inaccurate
return DisplayTimeText(max(1,amount))
-/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src, visible = TRUE)
+/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src, silent = FALSE)
+ mixture = target.return_analyzable_air()
+ if(!mixture)
+ return FALSE
+
var/icon = target
- if(visible)
- user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(user))] [target].", "You use the analyzer on [icon2html(icon, user)] [target].")
- var/results = "Results of analysis of [icon2html(icon, user)] [target]."
+ var/render_list = list()
+ if(!silent && isliving(user))
+ user.visible_message("[user] uses the analyzer on [icon2html(icon, viewers(user))] [target].", "You use the analyzer on [icon2html(icon, user)] [target].")
+ render_list += "Results of analysis of [icon2html(icon, user)] [target]."
var/list/airs = islist(mixture) ? mixture : list(mixture)
for(var/g in airs)
if(airs.len > 1) //not a unary gas mixture
- results += "\nNode [airs.Find(g)]"
+ render_list += "Node [airs.Find(g)]"
var/datum/gas_mixture/air_contents = g
var/total_moles = air_contents.total_moles()
@@ -744,30 +749,27 @@ GENETICS SCANNER
var/cached_scan_results = air_contents.analyzer_results
if(total_moles > 0)
- results += "\nMoles: [round(total_moles, 0.01)] mol"
- results += "\nVolume: [volume] L"
- results += "\nPressure: [round(pressure,0.01)] kPa"
+ //WS Start -- Atmos Analyzer Reformat (Issue #419)
+ render_list += "Moles: [round(total_moles, 0.01)] mol\
+ \nVolume: [volume] L\
+ \nPressure: [round(pressure,0.01)] kPa\
+ \nTemperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)"
+ //WS End
for(var/id in air_contents.get_gases())
- if(air_contents.get_moles(id) >= 0.005)
- var/gas_concentration = air_contents.get_moles(id)/total_moles
- results += "\n[GLOB.gas_data.names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)"
- results += "\nTemperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)"
+ var/gas_concentration = air_contents.get_moles(id)/total_moles
+ render_list += "[GLOB.gas_data.names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)" //WS Edit -- Atmos Analyzer Reformat (Issue #419)
else
- if(airs.len > 1)
- results += "\nThis node is empty!"
- else
- results += "\n[target] is empty!"
+ render_list += airs.len > 1 ? "This node is empty!" : "[target] is empty!"
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
- var/instability = round(cached_scan_results["fusion"], 0.01)
- var/tier = instability2text(instability)
- results += "\nLarge amounts of free neutrons detected in the air indicate that a fusion reaction took place."
- results += "\nInstability of the last fusion reaction: [instability]\n This indicates it was [tier]"
+ render_list += "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.\
+ \nInstability of the last fusion reaction: [round(cached_scan_results["fusion"], 0.01)]."
- to_chat(user, examine_block(results))
- return
+ // we let the join apply newlines so we do need handholding
+ to_chat(user, examine_block(jointext(render_list, "\n")), type = MESSAGE_TYPE_INFO)
+ return TRUE
/obj/item/analyzer/proc/scan_turf(mob/user, turf/location)
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index 230567d6ad12..6e8d340cfc9a 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -356,7 +356,7 @@
gender = NEUTER
var/knockdown = 0
-/obj/item/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
if(!..())
return
playsound(src.loc,'sound/weapons/bolathrow.ogg', 75, 1)
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index cc6e963473a4..7e7227d02bdd 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -378,7 +378,7 @@
throw_hit_chance = 99 //Have you prayed today?
custom_materials = list(/datum/material/iron = 10000, /datum/material/glass = 4000, /datum/material/silver = 10000, /datum/material/gold = 2000)
-/obj/item/melee/baton/boomerang/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force)
+/obj/item/melee/baton/boomerang/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, quickstart = TRUE)
if(turned_on)
if(ishuman(thrower))
var/mob/living/carbon/human/H = thrower
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index ad18d1049efb..e91d1b864f36 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -240,8 +240,8 @@
/obj/item/tank/return_air()
return air_contents
-// /obj/item/tank/return_analyzable_air()
-// return air_contents
+/obj/item/tank/return_analyzable_air()
+ return air_contents
/obj/item/tank/assume_air(datum/gas_mixture/giver)
air_contents.merge(giver)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 10b8c04367d1..554c96a52f44 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -99,7 +99,7 @@
/obj/proc/setAnchored(anchorvalue)
set_anchored(anchorvalue)
-/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
+/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE, quickstart = TRUE)
. = ..()
if(obj_flags & FROZEN)
visible_message("[src] shatters into a million pieces!")
diff --git a/code/game/objects/structures/crates_lockers/crates/critter.dm b/code/game/objects/structures/crates_lockers/crates/critter.dm
index 4b33f33c9931..101231c8d18f 100644
--- a/code/game/objects/structures/crates_lockers/crates/critter.dm
+++ b/code/game/objects/structures/crates_lockers/crates/critter.dm
@@ -34,8 +34,8 @@
if(manifest)
. += "manifest"
-/obj/structure/closet/crate/critter/return_air()
+/obj/structure/closet/crate/critter/return_analyzable_air()
if(tank)
- return tank.air_contents
+ return tank.return_analyzable_air()
else
- return loc.return_air()
+ return null
diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
index bb0682f07881..9ff4156adfa6 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
@@ -150,6 +150,9 @@
/obj/structure/transit_tube_pod/return_air()
return air_contents
+/obj/structure/transit_tube_pod/return_analyzable_air()
+ return air_contents
+
/obj/structure/transit_tube_pod/assume_air(datum/gas_mixture/giver)
return air_contents.merge(giver)
diff --git a/code/game/world.dm b/code/game/world.dm
index cabb344867ff..32ad111e251e 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -398,4 +398,3 @@ GLOBAL_LIST(topic_status_cache)
var/init_result = call_ext(library, "init")("block")
if (init_result != "0")
CRASH("Error initializing byond-tracy: [init_result]")
-
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index bc05d1095ae2..abacea242bbd 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -201,3 +201,9 @@
return
T.assume_air(air_contents)
air_update_turf()
+
+/obj/item/onetankbomb/return_analyzable_air()
+ if(bombtank)
+ return bombtank.return_analyzable_air()
+ else
+ return null
diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm
index cfa3e7eb14e3..7757c53ea674 100644
--- a/code/modules/atmospherics/environmental/LINDA_fire.dm
+++ b/code/modules/atmospherics/environmental/LINDA_fire.dm
@@ -10,7 +10,9 @@
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh)
- if(!air)
+ //If the air doesn't exist we just return false
+ var/list/air_gases = air?.get_gases()
+ if(!air_gases)
return
if (air.get_oxidation_power(exposed_temperature) < 0.5 || air.get_moles(GAS_HYPERNOB) > 5)
@@ -35,9 +37,11 @@
icon = 'icons/effects/fire.dmi'
icon_state = "1"
layer = GASFIRE_LAYER
+ blend_mode = BLEND_ADD
+ // light_system = MOVABLE_LIGHT
light_range = LIGHT_RANGE_FIRE
+ light_power = 1
light_color = LIGHT_COLOR_FIRE
- blend_mode = BLEND_ADD
var/volume = 125
var/temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
@@ -55,6 +59,11 @@
setDir(pick(GLOB.cardinals))
air_update_turf()
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+
/obj/effect/hotspot/proc/perform_exposure()
var/turf/open/location = loc
if(!istype(location) || !(location.air))
@@ -62,15 +71,18 @@
location.active_hotspot = src
- bypassing = volume > CELL_VOLUME*0.95
+ bypassing = volume > CELL_VOLUME*0.95 || location.air.return_temperature() >= FUSION_TEMPERATURE_THRESHOLD
if(bypassing)
+ if(temperature > location.air.return_temperature())
+ location.air.set_temperature(temperature) //now actually starts fires like intended
volume = location.air.reaction_results["fire"]*FIRE_GROWTH_RATE
temperature = location.air.return_temperature()
else
var/datum/gas_mixture/affected = location.air.remove_ratio(volume/location.air.return_volume())
if(affected) //in case volume is 0
- affected.set_temperature(temperature)
+ if(temperature > affected.return_temperature())
+ affected.set_temperature(temperature) //don't set the temperature lower than what it was
affected.react(src)
temperature = affected.return_temperature()
volume = affected.reaction_results["fire"]*FIRE_GROWTH_RATE
@@ -131,7 +143,7 @@
add_overlay(fusion_overlay)
add_overlay(rainbow_overlay)
- set_light(l_color = rgb(LERP(250,heat_r,greyscale_fire),LERP(160,heat_g,greyscale_fire),LERP(25,heat_b,greyscale_fire)))
+ set_light_color(rgb(LERP(250, heat_r, greyscale_fire), LERP(160, heat_g, greyscale_fire), LERP(25, heat_b, greyscale_fire)))
heat_r /= 255
heat_g /= 255
@@ -210,8 +222,8 @@
T.to_be_destroyed = FALSE
T.max_fire_temperature_sustained = 0
-/obj/effect/hotspot/Crossed(atom/movable/AM, oldLoc)
- ..()
+/obj/effect/hotspot/proc/on_entered(datum/source, atom/movable/AM, oldLoc)
+ SIGNAL_HANDLER
if(isliving(AM))
var/mob/living/L = AM
L.fire_act(temperature, volume)
diff --git a/code/modules/atmospherics/environmental/LINDA_system.dm b/code/modules/atmospherics/environmental/LINDA_system.dm
index a6a799753508..7c324a3f517a 100644
--- a/code/modules/atmospherics/environmental/LINDA_system.dm
+++ b/code/modules/atmospherics/environmental/LINDA_system.dm
@@ -18,7 +18,6 @@
/turf/open/CanAtmosPass(turf/T, vertical = FALSE)
var/dir = vertical? get_dir_multiz(src, T) : get_dir(src, T)
- var/opp = REVERSE_DIR(dir)
. = TRUE
if(vertical && !(zAirOut(dir, T) && T.zAirIn(dir, src)))
. = FALSE
@@ -26,15 +25,13 @@
. = FALSE
if (T == src)
return .
- for(var/atom/movable/AM as anything in contents+T.contents)
- var/turf/other = (AM.loc == src ? T : src)
- if(!(vertical? (CANVERTICALATMOSPASS(AM, other)) : (CANATMOSPASS(AM, other))))
+ for(var/obj/O in contents+T.contents)
+ var/turf/other = (O.loc == src ? T : src)
+ if(!(vertical? (CANVERTICALATMOSPASS(O, other)) : (CANATMOSPASS(O, other))))
. = FALSE
- if(AM.BlockThermalConductivity()) //the direction and open/closed are already checked on CanAtmosPass() so there are no arguments
- conductivity_blocked_directions |= dir
- T.conductivity_blocked_directions |= opp
- if(!.)
- return .
+
+/turf/proc/block_all_conductivity()
+ conductivity_blocked_directions |= NORTH | SOUTH | EAST | WEST | UP | DOWN
/atom/movable/proc/BlockThermalConductivity() // Objects that don't let heat through.
return FALSE
@@ -42,83 +39,117 @@
/turf/proc/ImmediateCalculateAdjacentTurfs()
var/canpass = CANATMOSPASS(src, src)
var/canvpass = CANVERTICALATMOSPASS(src, src)
+
+ conductivity_blocked_directions = 0
+
+ var/src_contains_firelock = 1
+ if(locate(/obj/machinery/door/firedoor) in src)
+ src_contains_firelock |= 2
+
+ var/list/atmos_adjacent_turfs = list()
+
for(var/direction in GLOB.cardinals_multiz)
- var/turf/T = get_step_multiz(src, direction)
- if(!istype(T))
+ var/turf/current_turf = get_step_multiz(src, direction)
+ if(!isopenturf(current_turf))
+ conductivity_blocked_directions |= direction
+
+ if(current_turf)
+ atmos_adjacent_turfs -= current_turf
+ LAZYREMOVE(current_turf.atmos_adjacent_turfs, src)
+
continue
- if(isopenturf(T) && !(blocks_air || T.blocks_air) && ((direction & (UP|DOWN))? (canvpass && CANVERTICALATMOSPASS(T, src)) : (canpass && CANATMOSPASS(T, src))) )
- LAZYINITLIST(atmos_adjacent_turfs)
- LAZYINITLIST(T.atmos_adjacent_turfs)
- atmos_adjacent_turfs[T] = ATMOS_ADJACENT_ANY
- T.atmos_adjacent_turfs[src] = ATMOS_ADJACENT_ANY
+
+ var/other_contains_firelock = 1
+ if(locate(/obj/machinery/door/firedoor) in current_turf)
+ other_contains_firelock |= 2
+
+ //Conductivity Update
+ var/opp = REVERSE_DIR(direction)
+ //all these must be above zero for auxmos to even consider them
+ if(!thermal_conductivity || !heat_capacity || !current_turf.thermal_conductivity || !current_turf.heat_capacity)
+ conductivity_blocked_directions |= direction
+ current_turf.conductivity_blocked_directions |= opp
else
- if (atmos_adjacent_turfs)
- atmos_adjacent_turfs -= T
- if (T.atmos_adjacent_turfs)
- T.atmos_adjacent_turfs -= src
- UNSETEMPTY(T.atmos_adjacent_turfs)
- T.set_sleeping(T.blocks_air)
- T.__update_auxtools_turf_adjacency_info(isspaceturf(T.get_z_base_turf()), -1)
+ for(var/obj/O in contents + current_turf.contents)
+ if(O.BlockThermalConductivity()) //the direction and open/closed are already checked on CanAtmosPass() so there are no arguments
+ conductivity_blocked_directions |= direction
+ current_turf.conductivity_blocked_directions |= opp
+ break
+ //End Conductivity Update
+
+ if(!(blocks_air || current_turf.blocks_air) && ((direction & (UP|DOWN))? (canvpass && CANVERTICALATMOSPASS(current_turf, src)) : (canpass && CANATMOSPASS(current_turf, src))))
+ atmos_adjacent_turfs[current_turf] = other_contains_firelock | src_contains_firelock
+ LAZYSET(current_turf.atmos_adjacent_turfs, src, src_contains_firelock)
+ else
+ atmos_adjacent_turfs -= current_turf
+ LAZYREMOVE(current_turf.atmos_adjacent_turfs, src)
+
+ current_turf.__update_auxtools_turf_adjacency_info()
UNSETEMPTY(atmos_adjacent_turfs)
src.atmos_adjacent_turfs = atmos_adjacent_turfs
- for(var/t in atmos_adjacent_turfs)
- var/turf/open/T = t
- for(var/obj/machinery/door/firedoor/FD in T)
- FD.UpdateAdjacencyFlags()
- for(var/obj/machinery/door/firedoor/FD in src)
- FD.UpdateAdjacencyFlags()
- __update_auxtools_turf_adjacency_info(isspaceturf(get_z_base_turf()))
-
-/turf/proc/set_sleeping(should_sleep)
-
-//returns a list of adjacent turfs that can share air with this one.
-//alldir includes adjacent diagonal tiles that can share
-// air with both of the related adjacent cardinal tiles
-/turf/proc/GetAtmosAdjacentTurfs(alldir = 0)
+ __update_auxtools_turf_adjacency_info()
+
+/turf/proc/clear_adjacencies()
+ block_all_conductivity()
+ for(var/turf/current_turf as anything in atmos_adjacent_turfs)
+ LAZYREMOVE(current_turf.atmos_adjacent_turfs, src)
+ current_turf.__update_auxtools_turf_adjacency_info()
+
+ LAZYNULL(atmos_adjacent_turfs)
+ __update_auxtools_turf_adjacency_info()
+
+/**
+ * Returns a list of adjacent turfs that can share air with this one.
+ * alldir includes adjacent diagonal tiles that can share
+ * air with both of the related adjacent cardinal tiles
+ */
+/turf/proc/GetAtmosAdjacentTurfs(alldir = FALSE)
var/adjacent_turfs
if (atmos_adjacent_turfs)
adjacent_turfs = atmos_adjacent_turfs.Copy()
else
- return list() // don't bother checking diagonals, diagonals are going to be cardinal checks anyways.
+ adjacent_turfs = list()
if (!alldir)
return adjacent_turfs
- var/turf/other
- var/turf/mid
- for (var/d in GLOB.diagonals)
- other = get_step(src, d)
- if(!other)
- continue
- // NS step
- mid = get_step(src, NSCOMPONENT(d))
- if((mid in adjacent_turfs) && (get_step(mid, EWCOMPONENT(d)) in adjacent_turfs))
- adjacent_turfs += other
- continue
- // EW step
- mid = get_step(src, EWCOMPONENT(d))
- if((mid in adjacent_turfs) && (get_step(mid, NSCOMPONENT(d)) in adjacent_turfs))
- adjacent_turfs += other
+ var/turf/curloc = src
+
+ for (var/direction in GLOB.diagonals_multiz)
+ var/matchingDirections = 0
+ var/turf/S = get_step_multiz(curloc, direction)
+ if(!S)
continue
+
+ for (var/checkDirection in GLOB.cardinals_multiz)
+ var/turf/checkTurf = get_step(S, checkDirection)
+ if(!S.atmos_adjacent_turfs || !S.atmos_adjacent_turfs[checkTurf])
+ continue
+
+ if (adjacent_turfs[checkTurf])
+ matchingDirections++
+
+ if (matchingDirections >= 2)
+ adjacent_turfs += S
+ break
+
return adjacent_turfs
-/atom/proc/air_update_turf(command = 0)
- if(!isturf(loc) && command)
- return
- var/turf/T = get_turf(loc)
- if(!istype(T))
+/atom/proc/air_update_turf(calculate_adjacencies = FALSE)
+ var/turf/location = get_turf(src)
+ if(!location)
return
+ location.air_update_turf(calculate_adjacencies)
- T.air_update_turf(command)
-
-/turf/air_update_turf(command = 0)
- if(command)
- ImmediateCalculateAdjacentTurfs()
+/turf/air_update_turf(calculate_adjacencies = FALSE)
+ if(!calculate_adjacencies)
+ return
+ ImmediateCalculateAdjacentTurfs()
/atom/movable/proc/move_update_air(turf/T)
- if(isturf(T))
- T.air_update_turf(1)
- air_update_turf(1)
+ if(isturf(T))
+ T.air_update_turf(TRUE)
+ air_update_turf(TRUE)
/atom/proc/atmos_spawn_air(text) //because a lot of people loves to copy paste awful code lets just make an easy proc to spawn your plasma fires
var/turf/open/T = get_turf(src)
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index c527d0d41631..d5c0a9fead1d 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -1,5 +1,4 @@
/turf
- //used for temperature calculations
//conductivity is divided by 10 when interacting with air for balance purposes
var/thermal_conductivity = 0.05
var/heat_capacity = 1
@@ -21,24 +20,18 @@
var/pressure_direction = 0
var/turf/pressure_specific_target
- var/datum/gas_mixture/turf/air
+ var/datum/gas_mixture/air
var/obj/effect/hotspot/active_hotspot
var/planetary_atmos = FALSE //air will revert to initial_gas_mix over time
var/list/atmos_overlay_types //gas IDs of current active gas overlays
-/turf/open/Initialize(mapload)
- if(!blocks_air)
- air = new(2500,src)
- air.copy_from_turf(src)
- if(planetary_atmos && !(initial_gas_mix in SSair.planetary))
- var/datum/gas_mixture/mix = new
- mix.parse_gas_string(initial_gas_mix)
- mix.mark_immutable()
- SSair.planetary[initial_gas_mix] = mix
- update_air_ref(planetary_atmos ? 1 : 2)
- . = ..()
+/turf/open/Initialize(mapload, inherited_virtual_z)
+ air = new(2500,src)
+ air.copy_from_turf(src)
+ update_air_ref(planetary_atmos ? AIR_REF_PLANETARY_TURF : AIR_REF_OPEN_TURF)
+ return ..()
/turf/open/Destroy()
if(active_hotspot)
@@ -102,10 +95,14 @@
RETURN_TYPE(/datum/gas_mixture)
return air
+/turf/open/return_analyzable_air()
+ return return_air()
+
/turf/temperature_expose()
if(return_temperature() > heat_capacity)
to_be_destroyed = TRUE
+
/turf/open/proc/eg_reset_cooldowns()
/turf/open/proc/eg_garbage_collect()
/turf/open/proc/get_excited()
@@ -127,6 +124,7 @@
src.atmos_overlay_types = null
return
+
for(var/id in air.get_gases())
if (nonoverlaying_gases[id])
continue
@@ -169,92 +167,79 @@
/////////////////////////////SIMULATION///////////////////////////////////
-/*#define LAST_SHARE_CHECK \
- var/last_share = our_air.get_last_share();\
- if(last_share > MINIMUM_AIR_TO_SUSPEND){\
- our_excited_group.reset_cooldowns();\
- cached_atmos_cooldown = 0;\
- } else if(last_share > MINIMUM_MOLES_DELTA_TO_MOVE) {\
- our_excited_group.dismantle_cooldown = 0;\
- cached_atmos_cooldown = 0;\
- }
-*/
/turf/proc/process_cell(fire_count)
-
/turf/open/proc/equalize_pressure_in_zone(cyclenum)
-/turf/open/proc/consider_firelocks(turf/T2)
- var/reconsider_adj = FALSE
- for(var/obj/machinery/door/firedoor/FD in T2)
- if((FD.flags_1 & ON_BORDER_1) && get_dir(T2, src) != FD.dir)
- continue
- FD.emergency_pressure_stop()
- reconsider_adj = TRUE
+
+/turf/proc/consider_firelocks(turf/T2) //TODO: Find out why this sometimes gets called. Possibly to do with atmos adjacency not being updated in auxmos?
+/turf/open/consider_firelocks(turf/T2)
+ if(blocks_air)
+ return
+ for(var/obj/machinery/airalarm/alarm in src)
+ alarm.handle_decomp_alarm()
for(var/obj/machinery/door/firedoor/FD in src)
- if((FD.flags_1 & ON_BORDER_1) && get_dir(src, T2) != FD.dir)
- continue
FD.emergency_pressure_stop()
- reconsider_adj = TRUE
- if(reconsider_adj)
- T2.ImmediateCalculateAdjacentTurfs() // We want those firelocks closed yesterday.
+ for(var/obj/machinery/door/firedoor/FD in T2)
+ FD.emergency_pressure_stop()
/turf/proc/handle_decompression_floor_rip()
+
/turf/open/floor/handle_decompression_floor_rip(sum)
- if(sum > 20 && prob(clamp(sum / 10, 0, 30)))
+ if(!blocks_air && sum > 20 && prob(clamp(sum / 10, 0, 30)))
remove_tile()
/turf/open/process_cell(fire_count)
//////////////////////////SPACEWIND/////////////////////////////
-/turf/proc/consider_pressure_difference()
+/turf/proc/consider_pressure_difference(turf/T, difference)
return
/turf/open/consider_pressure_difference(turf/T, difference)
+ SSair.high_pressure_delta |= src
if(difference > pressure_difference)
pressure_direction = get_dir(src, T)
pressure_difference = difference
- SSair.high_pressure_delta[src] = TRUE
/turf/open/proc/high_pressure_movements()
- var/atom/movable/M
+ if(blocks_air)
+ return
var/multiplier = 1
if(locate(/obj/structure/rack) in src)
multiplier *= 0.1
else if(locate(/obj/structure/table) in src)
multiplier *= 0.2
- for(var/thing in src)
- M = thing
- if (!M.anchored && !M.pulledby && M.last_high_pressure_movement_air_cycle < SSair.times_fired)
+ for(var/atom/movable/M as anything in src)
+ if(!M.anchored && !M.pulledby && M.last_high_pressure_movement_air_cycle < SSair.times_fired && (M.flags_1 & INITIALIZED_1) && !QDELETED(M))
M.experience_pressure_difference(pressure_difference * multiplier, pressure_direction, 0, pressure_specific_target)
if(pressure_difference > 100)
new /obj/effect/temp_visual/dir_setting/space_wind(src, pressure_direction, clamp(round(sqrt(pressure_difference) * 2), 10, 255))
-
/atom/movable/var/pressure_resistance = 10
/atom/movable/var/last_high_pressure_movement_air_cycle = 0
/atom/movable/proc/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0, throw_target)
- set waitfor = FALSE
- if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_PRESSURE_PUSH) & COMSIG_MOVABLE_BLOCKS_PRESSURE)
- return
-
var/const/PROBABILITY_OFFSET = 40
var/const/PROBABILITY_BASE_PRECENT = 10
var/max_force = sqrt(pressure_difference)*(MOVE_FORCE_DEFAULT / 5)
+ set waitfor = 0
var/move_prob = 100
- if(pressure_resistance > 0)
+ if (pressure_resistance > 0)
move_prob = (pressure_difference/pressure_resistance*PROBABILITY_BASE_PRECENT)-PROBABILITY_OFFSET
move_prob += pressure_resistance_prob_delta
- if(move_prob > PROBABILITY_OFFSET && prob(move_prob) && (move_resist != INFINITY) && (!anchored && (max_force >= (move_resist * MOVE_FORCE_PUSH_RATIO))) || (anchored && (max_force >= (move_resist * MOVE_FORCE_FORCEPUSH_RATIO))))
+ if (move_prob > PROBABILITY_OFFSET && prob(move_prob) && (move_resist != INFINITY) && (!anchored && (max_force >= (move_resist * MOVE_FORCE_PUSH_RATIO))) || (anchored && (max_force >= (move_resist * MOVE_FORCE_FORCEPUSH_RATIO))))
var/move_force = max_force * clamp(move_prob, 0, 100) / 100
+ if(ismob(src))
+ var/mob/M = src
+ if(M.mob_negates_gravity())
+ move_force = 0
if(move_force > 6000)
// WALLSLAM HELL TIME OH BOY
var/turf/throw_turf = get_ranged_target_turf(get_turf(src), direction, round(move_force / 2000))
if(throw_target && (get_dir(src, throw_target) & direction))
throw_turf = get_turf(throw_target)
var/throw_speed = clamp(round(move_force / 3000), 1, 10)
- throw_at(throw_turf, move_force / 3000, throw_speed)
- else
+ throw_at(throw_turf, move_force / 3000, throw_speed, quickstart = FALSE)
+ else if(move_force > 0)
step(src, direction)
last_high_pressure_movement_air_cycle = SSair.times_fired
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 0c1dcd22be55..fa991d703045 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -78,6 +78,8 @@
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 0, BIO = 100, RAD = 100, FIRE = 90, ACID = 30)
resistance_flags = FIRE_PROOF
+ COOLDOWN_DECLARE(decomp_alarm)
+
var/danger_level = 0
var/mode = AALARM_MODE_SCRUBBING
@@ -948,6 +950,14 @@
new /obj/item/stack/cable_coil(loc, 3)
qdel(src)
+/obj/machinery/airalarm/proc/handle_decomp_alarm()
+ if(!is_operational() || !COOLDOWN_FINISHED(src, decomp_alarm))
+ return
+ var/area/A = get_base_area(src)
+ A.firealert(src)
+ playsound(loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
+ COOLDOWN_START(src, decomp_alarm, 1 SECONDS)
+
#undef AALARM_MODE_SCRUBBING
#undef AALARM_MODE_VENTING
#undef AALARM_MODE_PANIC
diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm
index f8866877feae..7841e31f1057 100644
--- a/code/modules/atmospherics/machinery/components/components_base.dm
+++ b/code/modules/atmospherics/machinery/components/components_base.dm
@@ -156,6 +156,11 @@
to_chat(user, "Access denied.")
return UI_CLOSE
+// Tool acts
+
+/obj/machinery/atmospherics/components/return_analyzable_air()
+ return airs
+
/obj/machinery/atmospherics/components/attack_ghost(mob/dead/observer/O)
. = ..()
atmosanalyzer_scan(airs, O, src, FALSE)
diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm
index beb0988f8dc3..245e6661d898 100644
--- a/code/modules/atmospherics/machinery/pipes/pipes.dm
+++ b/code/modules/atmospherics/machinery/pipes/pipes.dm
@@ -51,6 +51,11 @@
/obj/machinery/atmospherics/pipe/return_air()
return parent.air
+/obj/machinery/atmospherics/pipe/return_analyzable_air()
+ if(air_temporary)
+ return air_temporary
+ return parent.air
+
/obj/machinery/atmospherics/pipe/remove_air(amount)
return parent.air.remove(amount)
diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
index eb900b953a1d..65d3f7cef139 100644
--- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
+++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
@@ -40,6 +40,9 @@
/obj/machinery/portable_atmospherics/return_air()
return air_contents
+/obj/machinery/portable_atmospherics/return_analyzable_air()
+ return air_contents
+
/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/components/unary/portables_connector/new_port)
//Make sure not already connected to something else
if(connected_port || !new_port || new_port.connected_device)
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index 8342c4e948d5..4aeafe44744e 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -104,7 +104,7 @@
return Leap(AM)
return FALSE
-/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
if(!..())
return
if(stat == CONSCIOUS)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 64d5d0d7cb1b..62a7287a77a5 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1152,7 +1152,7 @@
return TRUE
return FALSE
-/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force)
+/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, quickstart = TRUE)
stop_pulling()
. = ..()
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index d36933909591..7b788350fc1d 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -94,7 +94,7 @@
return ..()
-/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback)
+/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback, quickstart = TRUE)
. = ..(target, range, speed, thrower, FALSE, diagonals_first, callback)
/obj/item/paperplane/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm
index 5e384bc62ff7..9b95d5ac9821 100644
--- a/code/modules/power/singularity/collector.dm
+++ b/code/modules/power/singularity/collector.dm
@@ -178,6 +178,12 @@
loaded_tank.analyzer_act(user, I)
return TRUE
+/obj/machinery/power/rad_collector/return_analyzable_air()
+ if(loaded_tank)
+ return loaded_tank.return_analyzable_air()
+ else
+ return null
+
/obj/machinery/power/rad_collector/examine(mob/user)
. = ..()
if(active)
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index 774f1820ea62..964eed22eb8e 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -345,7 +345,7 @@
M.electrocute_act(80, src, null, SHOCK_ILLUSION)
qdel(src)
-/obj/item/spellpacket/lightningbolt/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/spellpacket/lightningbolt/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
. = ..()
if(ishuman(thrower))
var/mob/living/carbon/human/H = thrower
diff --git a/modular_splurt/code/game/objects/items/lewd_items/shibola.dm b/modular_splurt/code/game/objects/items/lewd_items/shibola.dm
index ba585fa28b7e..f2fd9f5ef32f 100644
--- a/modular_splurt/code/game/objects/items/lewd_items/shibola.dm
+++ b/modular_splurt/code/game/objects/items/lewd_items/shibola.dm
@@ -34,7 +34,7 @@
else
return ..()
-/obj/item/shibola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/shibola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
if(!..())
return
playsound(src.loc,'sound/weapons/bolathrow.ogg', 75, 1)
diff --git a/modular_splurt/code/game/objects/items/toys.dm b/modular_splurt/code/game/objects/items/toys.dm
index 71b7218fd185..857b84d3cd00 100644
--- a/modular_splurt/code/game/objects/items/toys.dm
+++ b/modular_splurt/code/game/objects/items/toys.dm
@@ -98,7 +98,7 @@
if(vibrator && enabled)
throwforce = 60
-/obj/item/toy/beach_ball/syndicate/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/toy/beach_ball/syndicate/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
if(ishuman(thrower))
throwforce = 0
. = ..()
diff --git a/tgstation.dme b/tgstation.dme
index fb5bb8baed38..9987f3529b02 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -6,7 +6,7 @@
// END_INTERNALS
// BEGIN_FILE_DIR
-#define FILE_DIR .
+#define FILE_DIR .
// END_FILE_DIR
// BEGIN_PREFERENCES
diff --git a/tgui/packages/tgui-panel/chat/constants.js b/tgui/packages/tgui-panel/chat/constants.js
index 3c998973266e..f09156266632 100644
--- a/tgui/packages/tgui-panel/chat/constants.js
+++ b/tgui/packages/tgui-panel/chat/constants.js
@@ -65,7 +65,8 @@ export const MESSAGE_TYPES = [
type: MESSAGE_TYPE_INFO,
name: 'Info',
description: 'Non-urgent messages from the game and items',
- selector: '.notice:not(.pm), .adminnotice, .info, .sinister, .cult, .infoplain, .announce, .hear, .smallnotice, .holoparasite',
+ selector:
+ '.notice:not(.pm), .adminnotice, .info, .sinister, .cult, .infoplain, .announce, .hear, .smallnotice, .holoparasite, .boldnotice',
},
{
type: MESSAGE_TYPE_WARNING,