diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 9a72196f8d..a3430383ef 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -43,7 +43,9 @@
var/no_air = null
// var/list/lights // list of all lights on this area
var/list/all_doors = null //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area
+ var/list/all_arfgs = null //Similar, but a list of all arfgs adjacent to this area
var/firedoors_closed = 0
+ var/arfgs_active = 0
var/list/ambience = list()
var/list/forced_ambience = null
var/sound_env = STANDARD_STATION
@@ -128,10 +130,11 @@
return 1
return 0
-// Either close or open firedoors depending on current alert statuses
+// Either close or open firedoors and arfgs depending on current alert statuses
/area/proc/firedoors_update()
if(fire || party || atmosalm)
firedoors_close()
+ arfgs_activate()
// VOREStation Edit - Make the lights colored!
if(fire)
for(var/obj/machinery/light/L in src)
@@ -142,6 +145,7 @@
// VOREStation Edit End
else
firedoors_open()
+ arfgs_deactivate()
// VOREStation Edit - Put the lights back!
for(var/obj/machinery/light/L in src)
L.reset_alert()
@@ -175,6 +179,25 @@
spawn(0)
E.open()
+// Activate all retention fields!
+/area/proc/arfgs_activate()
+ if(!arfgs_active)
+ arfgs_active = TRUE
+ if(!all_arfgs)
+ return
+ for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs)
+ E.generate_field() //don't need to check powered state like doors, the arfgs handles it on its end
+ E.wasactive = TRUE
+
+// Deactivate retention fields!
+/area/proc/arfgs_deactivate()
+ if(arfgs_active)
+ arfgs_active = FALSE
+ if(!all_arfgs)
+ return
+ for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs)
+ E.disable_field()
+ E.wasactive = FALSE
/area/proc/fire_alert()
if(!fire)
diff --git a/code/game/machinery/atm_ret_field.dm b/code/game/machinery/atm_ret_field.dm
new file mode 100644
index 0000000000..9e9a2af168
--- /dev/null
+++ b/code/game/machinery/atm_ret_field.dm
@@ -0,0 +1,233 @@
+/obj/machinery/atmospheric_field_generator
+ name = "atmospheric retention field generator"
+ desc = "A floor-mounted piece of equipment that generates an atmosphere-retaining energy field when powered and activated. Linked to environmental alarm systems and will automatically activate when hazardous conditions are detected.
Note: prolonged immersion in active atmospheric retention fields may have negative long-term health consequences."
+ icon = 'icons/obj/atm_fieldgen.dmi'
+ icon_state = "arfg_off"
+ anchored = TRUE
+ opacity = FALSE
+ density = FALSE
+ power_channel = ENVIRON //so they shut off last
+ use_power = USE_POWER_IDLE
+ idle_power_usage = 10
+ active_power_usage = 2500
+ var/ispowered = TRUE
+ var/isactive = FALSE
+ var/wasactive = FALSE //controls automatic reboot after power-loss
+ var/alwaysactive = FALSE //for a special subtype
+
+ //how long it takes us to reboot if we're shut down by an EMP
+ var/reboot_delay_min = 50
+ var/reboot_delay_max = 75
+
+ var/hatch_open = FALSE
+ var/wires_intact = TRUE
+ var/list/areas_added
+ var/field_type = /obj/structure/atmospheric_retention_field
+ circuit = /obj/item/weapon/circuitboard/arf_generator
+
+/obj/machinery/atmospheric_field_generator/impassable
+ desc = "An older model of ARF-G that generates an impassable retention field. Works just as well as the modern variety, but is slightly more energy-efficient.
Note: prolonged immersion in active atmospheric retention fields may have negative long-term health consequences."
+ active_power_usage = 2000
+ field_type = /obj/structure/atmospheric_retention_field/impassable
+
+/obj/machinery/atmospheric_field_generator/perma
+ name = "static atmospheric retention field generator"
+ desc = "A floor-mounted piece of equipment that generates an atmosphere-retaining energy field when powered and activated. This model is designed to always be active, though the field will still drop from loss of power or electromagnetic interference.
Note: prolonged immersion in active atmospheric retention fields may have negative long-term health consequences."
+ alwaysactive = TRUE
+ active_power_usage = 2000
+
+/obj/machinery/atmospheric_field_generator/perma/impassable
+ active_power_usage = 1500
+ field_type = /obj/structure/atmospheric_retention_field/impassable
+
+/obj/machinery/atmospheric_field_generator/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(W.is_crowbar() && isactive)
+ if(!src) return
+ to_chat(user, "You can't open the ARF-G whilst it's running!")
+ return
+ if(W.is_crowbar() && !isactive)
+ if(!src) return
+ to_chat(user, "You [hatch_open? "close" : "open"] \the [src]'s access hatch.")
+ hatch_open = !hatch_open
+ update_icon()
+ if(alwaysactive && wires_intact)
+ generate_field()
+ return
+ if(hatch_open && W.is_multitool())
+ if(!src) return
+ to_chat(user, "You toggle \the [src]'s activation behavior to [alwaysactive? "emergency" : "always-on"].")
+ alwaysactive = !alwaysactive
+ update_icon()
+ return
+ if(hatch_open && W.is_wirecutter())
+ if(!src) return
+ to_chat(user, "You [wires_intact? "cut" : "mend"] \the [src]'s wires!")
+ wires_intact = !wires_intact
+ update_icon()
+ return
+ if(hatch_open && istype(W,/obj/item/weapon/weldingtool))
+ if(!src) return
+ var/obj/item/weapon/weldingtool/WT = W
+ if(!WT.isOn()) return
+ if(WT.get_fuel() < 5) // uses up 5 fuel.
+ to_chat(user, "You need more fuel to complete this task.")
+ return
+ user.visible_message("[user] starts to disassemble \the [src].", "You start to disassemble \the [src].")
+ playsound(src, WT.usesound, 50, 1)
+ if(do_after(user,15 * W.toolspeed))
+ if(!src || !user || !WT.remove_fuel(5, user)) return
+ to_chat(user, "You fully disassemble \the [src]. There were no salvageable parts.")
+ qdel(src)
+ return
+
+/obj/machinery/atmospheric_field_generator/perma/Initialize()
+ generate_field()
+
+/obj/machinery/atmospheric_field_generator/update_icon()
+ if(stat & BROKEN)
+ icon_state = "arfg_broken"
+ else if(hatch_open && wires_intact)
+ icon_state = "arfg_open_wires"
+ else if(hatch_open && !wires_intact)
+ icon_state = "arfg_open_wirescut"
+ else if(isactive)
+ icon_state = "arfg_on"
+ else
+ icon_state = "arfg_off"
+
+/obj/machinery/atmospheric_field_generator/power_change()
+ var/oldstat
+ ..()
+ if(!(stat & NOPOWER))
+ ispowered = 1
+ update_icon()
+ if(alwaysactive || wasactive) //reboot our field if we were on or are supposed to be always-on
+ generate_field()
+ if(stat != oldstat && isactive && (stat & NOPOWER))
+ ispowered = 0
+ disable_field()
+ update_icon()
+
+/obj/machinery/atmospheric_field_generator/emp_act()
+ . = ..()
+ disable_field() //shutting dowwwwwwn
+ if(alwaysactive || wasactive) //reboot after a short delay if we were online before
+ spawn(rand(reboot_delay_min,reboot_delay_max))
+ generate_field()
+
+/obj/machinery/atmospheric_field_generator/ex_act(severity)
+ switch(severity)
+ if(1)
+ disable_field()
+ qdel(src)
+ return
+ if(2)
+ stat |= BROKEN
+ update_icon()
+ src.visible_message("The ARF-G cracks and shatters!","You hear an uncomfortable metallic crunch.")
+ disable_field()
+ if(3)
+ emp_act()
+ return
+
+/obj/machinery/atmospheric_field_generator/proc/generate_field()
+ if(!ispowered || hatch_open || !wires_intact || isactive) //if it's not powered, the hatch is open, the wires are busted, or it's already on, don't do anything
+ return
+ else
+ isactive = 1
+ icon_state = "arfg_on"
+ new field_type (src.loc)
+ src.visible_message("The ARF-G crackles to life!","You hear an ARF-G coming online!")
+ update_use_power(USE_POWER_ACTIVE)
+ return
+
+/obj/machinery/atmospheric_field_generator/proc/disable_field()
+ if(isactive)
+ icon_state = "arfg_off"
+ for(var/obj/structure/atmospheric_retention_field/F in loc)
+ qdel(F)
+ src.visible_message("The ARF-G shuts down with a low hum.","You hear an ARF-G powering down.")
+ update_use_power(USE_POWER_IDLE)
+ isactive = 0
+ return
+
+/obj/machinery/atmospheric_field_generator/Initialize()
+ . = ..()
+ //Delete ourselves if we find extra mapped in arfgs
+ for(var/obj/machinery/atmospheric_field_generator/F in loc)
+ if(F != src)
+ log_debug("Duplicate ARFGS at [x],[y],[z]")
+ return INITIALIZE_HINT_QDEL
+
+ var/area/A = get_area(src)
+ ASSERT(istype(A))
+
+ LAZYADD(A.all_arfgs, src)
+ areas_added = list(A)
+
+ for(var/direction in cardinal)
+ A = get_area(get_step(src,direction))
+ if(istype(A) && !(A in areas_added))
+ LAZYADD(A.all_arfgs, src)
+ areas_added += A
+
+/obj/structure/atmospheric_retention_field
+ name = "atmospheric retention field"
+ desc = "A shimmering forcefield that keeps the good air inside and the bad air outside. This field has been modulated so that it doesn't impede movement or projectiles.
Note: prolonged immersion in active atmospheric retention fields may have negative long-term health consequences."
+ icon = 'icons/obj/atm_fieldgen.dmi'
+ icon_state = "arfg_field"
+ anchored = TRUE
+ density = FALSE
+ opacity = 0
+ plane = MOB_PLANE
+ layer = ABOVE_MOB_LAYER
+ //mouse_opacity = 0
+ can_atmos_pass = ATMOS_PASS_NO
+ var/basestate = "arfg_field"
+
+ light_range = 3
+ light_power = 1
+ light_color = "#FFFFFF"
+ light_on = TRUE
+
+/obj/structure/atmospheric_retention_field/update_icon()
+ cut_overlays() //overlays.Cut()
+ var/list/dirs = list()
+ for(var/obj/structure/atmospheric_retention_field/F in orange(src,1))
+ dirs += get_dir(src, F)
+
+ var/list/connections = dirs_to_corner_states(dirs)
+
+ icon_state = ""
+ for(var/i = 1 to 4)
+ var/image/I = image(icon, "[basestate][connections[i]]", dir = 1<<(i-1))
+ add_overlay(I)
+
+ return
+
+/obj/structure/atmospheric_retention_field/Initialize()
+ . = ..()
+ update_nearby_tiles() //Force ZAS update
+ update_connections(1)
+ update_icon()
+
+/obj/structure/atmospheric_retention_field/Destroy()
+ for(var/obj/structure/atmospheric_retention_field/W in orange(1, src.loc))
+ W.update_connections(1)
+ update_nearby_tiles() //Force ZAS update
+ . = ..()
+
+/obj/structure/atmospheric_retention_field/attack_hand(mob/user as mob)
+ if(density)
+ visible_message("You touch the retention field, and it crackles faintly. Tingly!")
+ else
+ visible_message("You try to touch the retention field, but pass through it like it isn't even there.")
+
+/obj/structure/atmospheric_retention_field/ex_act()
+ return
+
+/obj/structure/atmospheric_retention_field/impassable
+ desc = "A shimmering forcefield that keeps the good air inside and the bad air outside. It seems fairly solid, almost like it's made out of some kind of hardened light.
Note: prolonged immersion in active atmospheric retention fields may have negative long-term health consequences."
+ icon = 'icons/obj/atm_fieldgen.dmi'
+ icon_state = "arfg_field"
+ density = TRUE
\ No newline at end of file
diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm
index ed4b086e73..d651ad2025 100644
--- a/code/game/machinery/frame.dm
+++ b/code/game/machinery/frame.dm
@@ -221,6 +221,11 @@
frame_style = FRAME_STYLE_WALL
x_offset = 28
y_offset = 28
+
+/datum/frame/frame_types/arfgs
+ name = "ARF Generator"
+ frame_class = FRAME_CLASS_MACHINE
+ frame_size = 3
//////////////////////////////
// Frame Object (Structure)
diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm
index a3c755a3ac..db3106e635 100644
--- a/code/game/objects/items/weapons/circuitboards/frame.dm
+++ b/code/game/objects/items/weapons/circuitboards/frame.dm
@@ -261,3 +261,13 @@
/obj/item/weapon/stock_parts/spring = 1,
/obj/item/stack/cable_coil = 5)
+/obj/item/weapon/circuitboard/arf_generator
+ name = T_BOARD("atmospheric field generator")
+ build_path = /obj/machinery/atmospheric_field_generator
+ board_type = new /datum/frame/frame_types/arfgs
+ origin_tech = list(TECH_MAGNET = 4, TECH_POWER = 4, TECH_BIO = 3)
+ req_components = list(
+ /obj/item/weapon/stock_parts/micro_laser/high = 2, //field emitters
+ /obj/item/weapon/stock_parts/scanning_module = 1, //atmosphere sensor
+ /obj/item/weapon/stock_parts/capacitor/adv = 1, //for the JUICE
+ /obj/item/stack/cable_coil = 10)
\ No newline at end of file
diff --git a/code/modules/research/designs/circuits/circuits.dm b/code/modules/research/designs/circuits/circuits.dm
index 5c77c19e60..9e21b9bc16 100644
--- a/code/modules/research/designs/circuits/circuits.dm
+++ b/code/modules/research/designs/circuits/circuits.dm
@@ -427,6 +427,13 @@ CIRCUITS BELOW
build_path = /obj/item/weapon/circuitboard/skills
sort_string = "LAAAC"
+/datum/design/circuit/arf_generator
+ name = "atmospheric field generator"
+ id = "arf_generator"
+ req_tech = list(TECH_MAGNET = 4, TECH_POWER = 4, TECH_BIO = 3)
+ build_path = /obj/item/weapon/circuitboard/arf_generator
+ sort_string = "LAAAD"
+
/datum/design/circuit/mecha
req_tech = list(TECH_DATA = 3)
diff --git a/icons/obj/atm_fieldgen.dmi b/icons/obj/atm_fieldgen.dmi
new file mode 100644
index 0000000000..0c30371196
Binary files /dev/null and b/icons/obj/atm_fieldgen.dmi differ
diff --git a/icons/obj/stock_parts.dmi b/icons/obj/stock_parts.dmi
index 64184432c7..1f203a1e8f 100644
Binary files a/icons/obj/stock_parts.dmi and b/icons/obj/stock_parts.dmi differ
diff --git a/vorestation.dme b/vorestation.dme
index bea44f2300..71d9cd9ede 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -831,7 +831,12 @@
#include "code\game\machinery\ai_slipper.dm"
#include "code\game\machinery\air_alarm.dm"
#include "code\game\machinery\airconditioner_vr.dm"
+<<<<<<< HEAD
#include "code\game\machinery\airconditioner_yw.dm"
+||||||| parent of 3b5fb11bd1... Merge pull request #11663 from KillianKirilenko/kk-arfg
+=======
+#include "code\game\machinery\atm_ret_field.dm"
+>>>>>>> 3b5fb11bd1... Merge pull request #11663 from KillianKirilenko/kk-arfg
#include "code\game\machinery\atmo_control.dm"
#include "code\game\machinery\autolathe.dm"
#include "code\game\machinery\Beacon.dm"