Files
Yogstation/code/game/machinery/airlock_cycle_control.dm
SapphicOverload 4b42b21acb Atom-level integrity + wall destruction rework (#21336)
Moves integrity to the atom level (ported from tgstation/tgstation#61183), and allows turfs to be destroyed in the same way objects can.

Many things which destroy walls have been reworked to use the integrity system instead, and walls can now be destroyed through conventional means like hitting them with something strong. They can also be repaired with welding tools while not on harm intent. Reinforced walls are still very strong and require powerful tools or weapons to damage. Also changes some demolition modifiers slightly to fit the new system: emitters have 2.4x, overcharged emitters have 4x, and pulse rifles have 6x.

Standard wall - 300 integrity, 20 damage deflection, 60 melee/bullet/laser armor

Reinforced wall - 400 integrity, 75 damage deflection, 80 melee/bullet armor, 60 laser armor
2024-03-04 04:56:46 +00:00

833 lines
28 KiB
Plaintext

// Embedded controller is great and all, but it is really unwieldy to map with. In addition, you can't build it in-game.
// This serves to make it really easy to make it really easy to make cycling airlocks both in-game and in the map editor.
// Instead of editing vars, this involves placing a couple of mapping helpers.
// also can I say how much I hate the whole radio control thing in this game. It's not even exposed to the player at all.
// All it does is making coding a massive pain in the rear end.
// Anyways for a functioning airlock, you need an interior and an exterior door. Vents are optional.
// If setup right, you can even make an airlock that cycles between two rooms of different atmospheres!
// Perfect for the plasmaman atmos tech.
// NOTE FOR MAPPERS:
// PLEASE DON'T PUT THIS ON THE SAME TILE AS A VENT IF THE AIRLOCK IS BIGGER THAN 1X1.
// (If this is a 1x2 airlock and there is a pressurizing and a depressurizing vent
// then put the depressurizing vent under the controller)
#define AIRLOCK_CYCLESTATE_INOPEN 0
#define AIRLOCK_CYCLESTATE_INOPENING 1
#define AIRLOCK_CYCLESTATE_INCLOSING 2
#define AIRLOCK_CYCLESTATE_CLOSED 3
#define AIRLOCK_CYCLESTATE_OUTCLOSING 4
#define AIRLOCK_CYCLESTATE_OUTOPENING 5
#define AIRLOCK_CYCLESTATE_OUTOPEN 6
#define AIRLOCK_CYCLESTATE_DOCKED -1
#define AIRLOCK_CYCLESTATE_ERROR -2
#define AIRLOCK_CYCLEROLE_INT_PRESSURIZE 1
#define AIRLOCK_CYCLEROLE_INT_DEPRESSURIZE 2
#define AIRLOCK_CYCLEROLE_EXT_PRESSURIZE 4
#define AIRLOCK_CYCLEROLE_EXT_DEPRESSURIZE 8
/obj/item/electronics/advanced_airlock_controller
name = "airlock controller electronics"
custom_price = 5
icon_state = "airalarm_electronics"
/obj/item/wallframe/advanced_airlock_controller
name = "airlock controller frame"
desc = "Used for building advanced airlock controllers."
icon = 'icons/obj/monitors.dmi'
icon_state = "aac_bitem"
result_path = /obj/machinery/advanced_airlock_controller
/obj/machinery/advanced_airlock_controller
name = "advanced airlock controller"
desc = "A machine designed to control the operation of cycling airlocks"
icon = 'icons/obj/monitors.dmi'
icon_state = "aac"
use_power = IDLE_POWER_USE
idle_power_usage = 4
active_power_usage = 8
power_channel = AREA_USAGE_ENVIRON
req_access = list(ACCESS_ATMOSPHERICS)
max_integrity = 250
integrity_failure = 80
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 0, BIO = 100, RAD = 100, FIRE = 90, ACID = 30)
resistance_flags = FIRE_PROOF
FASTDMM_PROP(\
set_instance_vars(\
pixel_x = (dir & 3)? INSTANCE_VAR_DEFAULT : (dir == 4 ? -24 : 24),\
pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : INSTANCE_VAR_DEFAULT\
),\
dir_amount = 4\
)
var/cyclestate = AIRLOCK_CYCLESTATE_INOPEN
var/interior_pressure = ONE_ATMOSPHERE
var/exterior_pressure = 0
var/locked = TRUE
var/aidisabled = 0
var/shorted = 0
var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone
var/config_error_str = "Needs Scan"
var/scan_on_late_init = FALSE
var/depressurization_margin = 10 // use a lower value to reduce cross-contamination
var/depressurization_target = 0 // What to target - should be lower than the depressurization margin
var/overlays_hash = null
var/skip_delay = 100
var/skip_timer = 0
var/is_skipping = FALSE
var/list/airlocks = list()
var/list/vents = list()
var/obj/vis_target = null
/obj/machinery/advanced_airlock_controller/lavaland
exterior_pressure = 36.896 // according to very reliable asay this is the correct pressure.
// lavaland isn't exactly a contaminant so I'll let it have a high depressurization margin
// (also miners dont appreciate vacuum)
depressurization_margin = 36.896
depressurization_target = 20
/obj/machinery/advanced_airlock_controller/mix_chamber
depressurization_margin = 0.15 // The minimum - We really don't want contamination.
/obj/machinery/advanced_airlock_controller/New(loc, ndir, nbuild)
..()
wires = new /datum/wires/advanced_airlock_controller(src)
if(ndir)
setDir(ndir)
if(nbuild)
buildstage = 0
panel_open = TRUE
pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : 0
update_cycle_icon()
/obj/machinery/advanced_airlock_controller/Destroy()
qdel(wires)
wires = null
cut_links()
SSair.stop_processing_machine(src)
return ..()
/obj/machinery/advanced_airlock_controller/Initialize(mapload)
. = ..()
SSair.start_processing_machine(src)
scan_on_late_init = mapload
if(mapload && (. != INITIALIZE_HINT_QDEL))
return INITIALIZE_HINT_LATELOAD
/obj/machinery/advanced_airlock_controller/LateInitialize()
. = ..()
if(scan_on_late_init)
scan(TRUE)
update_error_status()
update_docked_status(FALSE)
for(var/A in airlocks)
var/obj/machinery/door/airlock/airlock = A
if(airlock.density && (cyclestate == AIRLOCK_CYCLESTATE_CLOSED || (airlocks[A] && cyclestate == AIRLOCK_CYCLESTATE_INOPEN) || (!airlocks[A] && cyclestate == AIRLOCK_CYCLESTATE_OUTOPEN)))
airlock.bolt()
/obj/machinery/advanced_airlock_controller/proc/update_cycle_icon(use_hash = FALSE)
if(!isopenturf(get_turf(src)))
return
var/pressure = 0
var/datum/gas_mixture/environment = return_air()
if(environment)
pressure = environment.return_pressure()
var/maxpressure = (exterior_pressure && (cyclestate == AIRLOCK_CYCLESTATE_OUTCLOSING || cyclestate == AIRLOCK_CYCLESTATE_OUTOPENING || cyclestate == AIRLOCK_CYCLESTATE_OUTOPEN)) ? exterior_pressure : interior_pressure
var/pressure_bars = ROUND_UP(pressure / maxpressure * 5 + 0.01)
var/new_overlays_hash = "[pressure_bars]-[cyclestate]-[buildstage]-[panel_open]-[stat]-[shorted]-[locked]-\ref[vis_target]"
if(use_hash && new_overlays_hash == overlays_hash)
return
overlays_hash = new_overlays_hash
cut_overlays()
if(panel_open)
switch(buildstage)
if(2)
icon_state = "aac_b3"
if(1)
icon_state = "aac_b2"
if(0)
icon_state = "aac_b1"
return
icon_state = "aac"
if((stat & (NOPOWER|BROKEN)) || shorted)
return
var/is_exterior_pressure = (cyclestate == AIRLOCK_CYCLESTATE_OUTCLOSING || cyclestate == AIRLOCK_CYCLESTATE_OUTOPENING || cyclestate == AIRLOCK_CYCLESTATE_OUTOPEN)
add_overlay("aac_[is_exterior_pressure ? "ext" : "int"]p_[pressure_bars]")
add_overlay("aac_cyclestate_[cyclestate]")
if(obj_flags & EMAGGED)
add_overlay("aac_emagged")
else if(!locked)
add_overlay("aac_unlocked")
if(vis_target)
var/f_dx = ((vis_target.pixel_x - pixel_x) / world.icon_size) + (vis_target.x - x)
var/f_dy = ((vis_target.pixel_y - pixel_y) / world.icon_size) + (vis_target.y - y)
var/dist = sqrt(f_dx*f_dx+f_dy*f_dy)
var/s_dx = f_dy/dist
var/s_dy = -f_dx/dist
var/matrix/TR = new
TR.Translate(0, 16)
TR.Multiply(new /matrix(s_dx, f_dx, 0, s_dy, f_dy, 0))
var/mutable_appearance/M = mutable_appearance(icon, "hologram-line", ABOVE_LIGHTING_PLANE)
M.transform = TR
add_overlay(M)
/obj/machinery/advanced_airlock_controller/proc/reset(wire)
switch(wire)
if(WIRE_POWER)
if(!wires.is_cut(WIRE_POWER))
shorted = FALSE
update_cycle_icon()
if(WIRE_AI)
if(!wires.is_cut(WIRE_AI))
aidisabled = FALSE
/obj/machinery/advanced_airlock_controller/proc/shock(mob/user, prb)
if((stat & (NOPOWER))) // unpowered, no shock
return 0
if(!prob(prb))
return 0 //you lucked out, no shock for you
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(5, 1, src)
s.start() //sparks always.
if (electrocute_mob(user, get_area(src), src, 1, TRUE))
return 1
else
return 0
/obj/machinery/advanced_airlock_controller/proc/update_docked_status(process_on_changed = FALSE)
if(cyclestate == AIRLOCK_CYCLESTATE_ERROR)
return
var/is_docked = FALSE
for(var/A in airlocks)
var/obj/machinery/door/airlock/airlock = A
if(!airlocks[A]) // only exterior airlocks are checked for docks
continue
var/turf/T = get_turf(airlock)
if(!T)
continue
for(var/cdir in GLOB.cardinals)
var/turf/T2 = get_step(T, cdir)
if(!T2)
continue
if(T2.loc != T.loc && (locate(/obj/machinery/door/airlock) in T2))
is_docked = TRUE
break
if(is_docked)
break
if(is_docked && cyclestate != AIRLOCK_CYCLESTATE_DOCKED)
cyclestate = AIRLOCK_CYCLESTATE_DOCKED
if(process_on_changed)
process_atmos()
if(!is_docked && cyclestate == AIRLOCK_CYCLESTATE_DOCKED)
cyclestate = AIRLOCK_CYCLESTATE_INOPENING
reset_skip()
for(var/airlock in airlocks)
coerce_door(airlock, TRUE)
if(process_on_changed)
process_atmos()
/obj/machinery/advanced_airlock_controller/proc/update_error_status()
if(!airlocks.len)
cyclestate = AIRLOCK_CYCLESTATE_ERROR
return
var/has_interior = FALSE
var/has_exterior = FALSE
for(var/A in airlocks)
if(airlocks[A] == 1)
has_exterior = TRUE
if(airlocks[A] == 0)
has_interior = TRUE
if(!has_interior || !has_exterior)
if(!has_interior)
config_error_str = "No interior door"
else if(!has_exterior)
config_error_str = "No exterior door"
cyclestate = AIRLOCK_CYCLESTATE_ERROR
return
if(cyclestate == AIRLOCK_CYCLESTATE_ERROR)
cyclestate = AIRLOCK_CYCLESTATE_CLOSED
update_docked_status()
/obj/machinery/advanced_airlock_controller/proc/coerce_door(obj/machinery/door/airlock/door, target_density = 0)
if(door.density == target_density && !door.operating)
door.bolt()
return TRUE
if(door.operating || door.welded || door.brace || !door.hasPower() || door.wires.is_cut(WIRE_BOLTS))
return FALSE
door.unbolt()
if(door.density != target_density)
if(target_density)
spawn(0)
door.close()
door.bolt()
else
spawn(0)
door.open()
door.bolt()
return FALSE
/obj/machinery/advanced_airlock_controller/proc/unbolt_door(obj/machinery/door/airlock/door)
if(!door.wires.is_cut(WIRE_BOLTS))
door.unbolt()
/obj/machinery/advanced_airlock_controller/process()
process_atmos()
/obj/machinery/advanced_airlock_controller/process_atmos()
if((stat & (NOPOWER|BROKEN)) || shorted)
update_cycle_icon(TRUE)
return
if(!isopenturf(get_turf(src)))
update_cycle_icon(TRUE)
return
var/pressure = 0
var/datum/gas_mixture/environment = return_air()
if(environment)
pressure = environment.return_pressure()
update_error_status()
var/doors_valid = TRUE
var/vents_valid = TRUE
switch(cyclestate)
if(AIRLOCK_CYCLESTATE_ERROR)
return
if(AIRLOCK_CYCLESTATE_CLOSED)
return
if(AIRLOCK_CYCLESTATE_DOCKED)
for(var/airlock in airlocks)
unbolt_door(airlock)
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
if(vents[vent] & AIRLOCK_CYCLEROLE_INT_PRESSURIZE)
vent.pump_direction = 1
vent.pressure_checks = 1
vent.external_pressure_bound = interior_pressure
vent.on = TRUE
vent.update_appearance(UPDATE_ICON)
else
vent.on = FALSE
vent.update_appearance(UPDATE_ICON)
return
if(AIRLOCK_CYCLESTATE_INCLOSING)
for(var/airlock in airlocks)
doors_valid = doors_valid && coerce_door(airlock, TRUE)
if(doors_valid || is_skipping)
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
if(vents[vent] & AIRLOCK_CYCLEROLE_INT_DEPRESSURIZE)
vent.pump_direction = 0
vent.pressure_checks = 1
vent.external_pressure_bound = depressurization_target
vents_valid = FALSE
vent.on = TRUE
vent.update_appearance(UPDATE_ICON)
else
vent.on = FALSE
vent.update_appearance(UPDATE_ICON)
if(pressure < depressurization_margin)
vents_valid = TRUE
if((doors_valid && vents_valid) || is_skipping)
cyclestate = AIRLOCK_CYCLESTATE_OUTOPENING
reset_skip()
if(AIRLOCK_CYCLESTATE_OUTCLOSING)
for(var/airlock in airlocks)
doors_valid = doors_valid && coerce_door(airlock, TRUE)
if(doors_valid || is_skipping)
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
if(vents[vent] & AIRLOCK_CYCLEROLE_EXT_DEPRESSURIZE)
vent.pump_direction = 0
vent.pressure_checks = 1
vent.external_pressure_bound = depressurization_target
vents_valid = FALSE
vent.on = TRUE
vent.update_appearance(UPDATE_ICON)
else
vent.on = FALSE
vent.update_appearance(UPDATE_ICON)
if(pressure < depressurization_margin)
vents_valid = TRUE
if(vents_valid || is_skipping)
cyclestate = AIRLOCK_CYCLESTATE_INOPENING
reset_skip()
if(AIRLOCK_CYCLESTATE_INOPENING)
for(var/airlock in airlocks)
if(airlocks[airlock])
doors_valid = doors_valid && coerce_door(airlock, 1)
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
if(vents[vent] & AIRLOCK_CYCLEROLE_INT_PRESSURIZE)
vent.pump_direction = 1
vent.pressure_checks = 1
vent.external_pressure_bound = interior_pressure
vents_valid = FALSE
vent.on = TRUE
vent.update_appearance(UPDATE_ICON)
else
vent.on = FALSE
vent.update_appearance(UPDATE_ICON)
if(pressure > interior_pressure - 0.5)
vents_valid = TRUE
if(vents_valid || is_skipping)
for(var/airlock in airlocks)
if(!airlocks[airlock])
doors_valid = doors_valid && coerce_door(airlock, 0)
if(doors_valid || is_skipping)
cyclestate = AIRLOCK_CYCLESTATE_INOPEN
reset_skip()
if(AIRLOCK_CYCLESTATE_OUTOPENING)
for(var/airlock in airlocks)
if(!airlocks[airlock])
doors_valid = doors_valid && coerce_door(airlock, 1)
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
if(vents[vent] & AIRLOCK_CYCLEROLE_EXT_PRESSURIZE)
vent.pump_direction = 1
vent.pressure_checks = 1
vent.external_pressure_bound = exterior_pressure
vents_valid = FALSE
vent.on = TRUE
vent.update_appearance(UPDATE_ICON)
else
vent.on = FALSE
vent.update_appearance(UPDATE_ICON)
if(pressure > exterior_pressure - 0.5)
vents_valid = TRUE
if(vents_valid || is_skipping)
for(var/airlock in airlocks)
if(airlocks[airlock])
doors_valid = doors_valid && coerce_door(airlock, 0)
if(doors_valid || is_skipping)
cyclestate = AIRLOCK_CYCLESTATE_OUTOPEN
reset_skip()
if(AIRLOCK_CYCLESTATE_INOPEN)
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
vent.on = FALSE
vent.update_appearance(UPDATE_ICON)
if(AIRLOCK_CYCLESTATE_OUTOPEN)
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
vent.on = FALSE
vent.update_appearance(UPDATE_ICON)
update_cycle_icon(TRUE)
/obj/machinery/advanced_airlock_controller/attackby(obj/item/W, mob/user, params)
switch(buildstage)
if(2)
if(W.tool_behaviour == TOOL_WIRECUTTER && panel_open && wires.is_all_cut())
W.play_tool_sound(src)
to_chat(user, span_notice("You cut the final wires."))
new /obj/item/stack/cable_coil(loc, 5)
buildstage = 1
update_cycle_icon()
return
else if(W.tool_behaviour == TOOL_SCREWDRIVER) // Opening that up.
W.play_tool_sound(src)
panel_open = !panel_open
to_chat(user, span_notice("The wires have been [panel_open ? "exposed" : "unexposed"]."))
update_cycle_icon()
return
else if(W.GetID())// trying to unlock the interface with an ID card
togglelock(user)
return
else if(panel_open && is_wire_tool(W))
wires.interact(user)
return
if(1)
if(W.tool_behaviour == TOOL_CROWBAR)
user.visible_message("[user.name] removes the electronics from [src.name].",\
span_notice("You start prying out the circuit..."))
W.play_tool_sound(src)
if (W.use_tool(src, user, 20))
if (buildstage == 1)
to_chat(user, span_notice("You remove the airlock controller electronics."))
new /obj/item/electronics/advanced_airlock_controller( src.loc )
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
buildstage = 0
update_cycle_icon()
return
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/cable = W
if(cable.get_amount() < 5)
to_chat(user, span_warning("You need five lengths of cable to wire the airlock controller!"))
return
user.visible_message("[user.name] wires the airlock controller.", \
span_notice("You start wiring the airlock controller..."))
if (do_after(user, 2 SECONDS, src))
if (cable.get_amount() >= 5 && buildstage == 1)
cable.use(5)
to_chat(user, span_notice("You wire the airlock controller."))
wires.repair()
aidisabled = 0
locked = FALSE
cyclestate = AIRLOCK_CYCLESTATE_ERROR
cut_links()
shorted = 0
buildstage = 2
update_cycle_icon()
return
if(0)
if(istype(W, /obj/item/electronics/advanced_airlock_controller))
if(user.temporarilyRemoveItemFromInventory(W))
to_chat(user, span_notice("You insert the circuit."))
buildstage = 1
update_cycle_icon()
qdel(W)
return
if(istype(W, /obj/item/electroadaptive_pseudocircuit))
var/obj/item/electroadaptive_pseudocircuit/P = W
if(!P.adapt_circuit(user, 25))
return
user.visible_message(span_notice("[user] fabricates a circuit and places it into [src]."), \
span_notice("You adapt an airlock controller circuit and slot it into the assembly."))
buildstage = 1
update_cycle_icon()
return
if(W.tool_behaviour == TOOL_WRENCH)
to_chat(user, span_notice("You detach \the [src] from the wall."))
W.play_tool_sound(src)
new /obj/item/wallframe/advanced_airlock_controller( user.loc )
qdel(src)
return
return ..()
/obj/machinery/advanced_airlock_controller/proc/cut_links()
for(var/obj/machinery/door/airlock/A in airlocks)
if(A.aac == src)
A.aac = null
for(var/V in vents)
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
if(vent.aac == src)
vent.aac = null
airlocks.Cut()
vents.Cut()
/obj/machinery/advanced_airlock_controller/proc/scan(assume_roles = FALSE)
cut_links()
config_error_str = "Unknown error (bug coders)"
var/turf/open/initial_turf = get_turf(src)
if(!istype(initial_turf))
config_error_str = "Scan blocked by wall"
return
var/list/turfs = list()
turfs[initial_turf] = 1
for(var/I = 1; I <= turfs.len; I++)
var/turf/open/T = turfs[I]
if(assume_roles)
T.immediate_calculate_adjacent_turfs()
for(var/turf/open/T2 in T.atmos_adjacent_turfs)
if(get_dist(initial_turf, T2) > 5)
config_error_str = "Airlock too big"
return
if(locate(/obj/machinery/door/airlock) in T2)
continue
turfs[T2] = 1
if(turfs.len > 16) // I will allow a 4x4 airlock for a shitty poor-man's spacepod bay.
config_error_str = "Airlock too big"
for(var/cdir in GLOB.cardinals)
var/turf/T2 = get_step(T, cdir)
for(var/obj/machinery/door/airlock/A in T2)
if(!A.aac || A.aac == src)
A.aac = src
airlocks[A] = 0
if(assume_roles)
for(var/adir in GLOB.cardinals)
var/turf/check_turf = get_step(T2, adir)
if(check_turf.loc != T2.loc)
airlocks[A] = 1
break
for(var/obj/machinery/atmospherics/components/unary/vent_pump/vent in T)
if(!vent.aac || vent.aac == src)
vent.aac = src
vents[vent] = 0
if(assume_roles)
if(istype(vent, /obj/machinery/atmospherics/components/unary/vent_pump/siphon))
vents[vent] = AIRLOCK_CYCLEROLE_INT_DEPRESSURIZE | AIRLOCK_CYCLEROLE_EXT_DEPRESSURIZE
else
vents[vent] = AIRLOCK_CYCLEROLE_INT_PRESSURIZE | AIRLOCK_CYCLEROLE_EXT_PRESSURIZE
for(var/obj/machinery/atmospherics/components/binary/dp_vent_pump/vent in T)
if(!vent.aac || vent.aac == src)
vent.aac = src
vents[vent] = 0
if(assume_roles)
vents[vent] = AIRLOCK_CYCLEROLE_INT_DEPRESSURIZE | AIRLOCK_CYCLEROLE_EXT_DEPRESSURIZE | AIRLOCK_CYCLEROLE_INT_PRESSURIZE | AIRLOCK_CYCLEROLE_EXT_PRESSURIZE
if(!airlocks.len)
config_error_str = "No airlocks"
return
config_error_str = null
/obj/machinery/advanced_airlock_controller/ui_status(mob/user)
if(user.has_unlimited_silicon_privilege && aidisabled)
to_chat(user, "AI control has been disabled.")
else if(!shorted)
return ..()
return UI_CLOSE
/obj/machinery/advanced_airlock_controller/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "AdvancedAirlockController", name)
ui.open()
/obj/machinery/advanced_airlock_controller/ui_data(mob/user)
var/pressure = 0
if(isopenturf(get_turf(src)))
var/datum/gas_mixture/environment = return_air()
if(environment)
pressure = environment.return_pressure()
var/data = list(
"locked" = locked,
"siliconUser" = user.has_unlimited_silicon_privilege,
"emagged" = (obj_flags & EMAGGED ? 1 : 0),
"cyclestate" = cyclestate,
"pressure" = pressure,
"maxpressure" = (exterior_pressure && (cyclestate == AIRLOCK_CYCLESTATE_OUTCLOSING || cyclestate == AIRLOCK_CYCLESTATE_OUTOPENING || cyclestate == AIRLOCK_CYCLESTATE_OUTOPEN)) ? exterior_pressure : interior_pressure,
"vents" = list(),
"airlocks" = list(),
"skip_timer" = (world.time - skip_timer),
"skip_delay" = skip_delay,
"vis_target" = "\ref[vis_target]"
)
if((locked && !user.has_unlimited_silicon_privilege) || (user.has_unlimited_silicon_privilege && aidisabled))
return data
data["config_error_str"] = config_error_str
data["interior_pressure"] = interior_pressure
data["exterior_pressure"] = exterior_pressure
data["depressurization_margin"] = depressurization_margin
data["depressurization_target"] = depressurization_target
for(var/V in vents)
// it could also be a dpvent.
var/obj/machinery/atmospherics/components/unary/vent_pump/vent = V
data["vents"] += list(list(
"role" = vents[vent],
"vent_id" = "\ref[vent]",
"name" = vent.name
))
for(var/A in airlocks)
var/obj/machinery/door/airlock/airlock = A
var/access_str = "None"
airlock.gen_access()
if(islist(airlock.req_access) && airlock.req_access.len)
access_str = airlock.req_access.len > 1 ? "All of " : ""
for(var/I in 1 to airlock.req_access.len)
if(I != 1)
access_str += ", "
access_str += get_access_desc(airlock.req_access[I])
if(islist(airlock.req_one_access) && airlock.req_one_access.len)
access_str = airlock.req_one_access.len > 1 ? "One of " : ""
for(var/I in 1 to airlock.req_one_access.len)
if(I != 1)
access_str += ", "
access_str += get_access_desc(airlock.req_one_access[I])
data["airlocks"] += list(list(
"role" = airlocks[airlock],
"airlock_id" = "\ref[airlock]",
"name" = airlock.name,
"access" = access_str
))
return data
/obj/machinery/advanced_airlock_controller/ui_close()
. = ..()
vis_target = null
/obj/machinery/advanced_airlock_controller/ui_act(action, params)
if(..() || buildstage != 2)
return
// these actions can be done by anyone
switch(action)
if("cycle")
var/is_allowed = TRUE
for(var/obj/machinery/door/airlock/A in airlocks)
if(!A.allowed(usr))
if(is_allowed)
is_allowed = FALSE
to_chat(usr, span_danger("Access denied."))
if(A.density)
spawn()
A.do_animate("deny")
if(is_allowed)
cycle_to(text2num(params["exterior"]))
if("skip")
if((world.time - skip_timer) >= skip_delay && (cyclestate == AIRLOCK_CYCLESTATE_OUTCLOSING || cyclestate == AIRLOCK_CYCLESTATE_OUTOPENING || cyclestate == AIRLOCK_CYCLESTATE_INOPENING || cyclestate == AIRLOCK_CYCLESTATE_INCLOSING))
is_skipping = TRUE
if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled))
return
switch(action)
if("lock")
if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
. = TRUE
vis_target = null
if("toggle_role")
var/vent = locate(params["vent_id"])
if(vent == null || vents[vent] == null)
return
var/curr_role = vents[vent]
var/role_to_toggle = text2num(params["val"]) & 15
if(curr_role & role_to_toggle)
vents[vent] = curr_role & ~(role_to_toggle)
else
vents[vent] = curr_role | role_to_toggle
if("set_airlock_role")
var/airlock = locate(params["airlock_id"])
if(airlock == null || airlocks[airlock] == null)
return
airlocks[airlock] = !!text2num(params["val"])
if("clear_vis")
vis_target = null
if("set_vis_vent")
var/vent = locate(params["vent_id"])
if(vent == null || vents[vent] == null)
return
vis_target = vent
if("set_vis_airlock")
var/airlock = locate(params["airlock_id"])
if(airlock == null || airlocks[airlock] == null)
return
vis_target = airlock
if("scan")
scan()
if("interior_pressure")
interior_pressure = clamp(text2num(params["pressure"]), 0, ONE_ATMOSPHERE)
if("exterior_pressure")
exterior_pressure = clamp(text2num(params["pressure"]), 0, ONE_ATMOSPHERE)
if("depressurization_margin")
depressurization_margin = clamp(text2num(params["pressure"]), 0.15, 40)
if(depressurization_target > depressurization_margin - 0.15)
depressurization_target = depressurization_margin - 0.15
if("depressurization_target")
depressurization_target = clamp(text2num(params["pressure"]), 0, depressurization_margin - 0.15)
if("skip_delay")
skip_delay = clamp(text2num(params["skip_delay"]), 0, 1200)
update_cycle_icon(TRUE)
/obj/machinery/advanced_airlock_controller/proc/request_from_door(airlock)
var/role = airlocks[airlock]
if(role == null)
return
cycle_to(role)
/obj/machinery/advanced_airlock_controller/proc/cycle_to(exterior)
if(!exterior)
if(cyclestate == AIRLOCK_CYCLESTATE_OUTOPEN || cyclestate == AIRLOCK_CYCLESTATE_CLOSED || cyclestate == AIRLOCK_CYCLESTATE_OUTOPENING)
cyclestate = AIRLOCK_CYCLESTATE_OUTCLOSING
reset_skip()
process_atmos()
else if(cyclestate == AIRLOCK_CYCLESTATE_INCLOSING)
cyclestate = AIRLOCK_CYCLESTATE_INOPENING
reset_skip()
process_atmos()
else
if(cyclestate == AIRLOCK_CYCLESTATE_INOPEN || cyclestate == AIRLOCK_CYCLESTATE_CLOSED || cyclestate == AIRLOCK_CYCLESTATE_INOPENING)
cyclestate = AIRLOCK_CYCLESTATE_INCLOSING
reset_skip()
else if(cyclestate == AIRLOCK_CYCLESTATE_OUTCLOSING)
cyclestate = AIRLOCK_CYCLESTATE_OUTOPENING
reset_skip()
/obj/machinery/advanced_airlock_controller/proc/reset_skip()
is_skipping = FALSE
skip_timer = world.time
/obj/machinery/advanced_airlock_controller/AltClick(mob/user)
..()
if(!user.canUseTopic(src, !issilicon(user)) || !isturf(loc))
return
else
togglelock(user)
/obj/machinery/advanced_airlock_controller/proc/togglelock(mob/living/user)
if(stat & (NOPOWER|BROKEN))
to_chat(user, span_warning("It does nothing!"))
else
if(src.allowed(usr) && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
update_cycle_icon()
to_chat(user, span_notice("You [ locked ? "lock" : "unlock"] the airlock controller interface."))
updateUsrDialog()
else
to_chat(user, span_danger("Access denied."))
return
/obj/machinery/advanced_airlock_controller/power_change()
. = ..()
update_cycle_icon()
/obj/machinery/advanced_airlock_controller/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
return FALSE
obj_flags |= EMAGGED
visible_message(span_warning("Sparks fly out of [src]!"), span_notice("You emag [src], disabling its safeties."))
playsound(src, "sparks", 50, 1)
return TRUE
/obj/machinery/advanced_airlock_controller/atom_break(damage_flag)
..()
update_cycle_icon()
/obj/machinery/advanced_airlock_controller/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
new /obj/item/stack/sheet/metal(loc, 2)
var/obj/item/I = new /obj/item/electronics/advanced_airlock_controller(loc)
if(!disassembled)
I.update_integrity(I.max_integrity * 0.5)
new /obj/item/stack/cable_coil(loc, 3)
qdel(src)
/obj/machinery/door/airlock
var/obj/machinery/advanced_airlock_controller/aac
/obj/machinery/door/airlock/Initialize(mapload)
. = ..()
update_aac_docked()
/obj/machinery/door/airlock/Destroy()
var/turf/T = get_turf(src)
. = ..()
if(aac)
aac.airlocks -= src
aac = null
if(T)
update_aac_docked(T)
/obj/machinery/door/airlock/proc/update_aac_docked(atom/point = src)
if(aac)
aac.update_docked_status(TRUE)
var/turf/our_turf = get_turf(point)
if(!our_turf)
return
for(var/cdir in GLOB.cardinals)
var/turf/T = get_step(point, cdir)
if(!T || (T.loc == our_turf.loc))
continue
for(var/obj/machinery/door/airlock/A in T)
if(A.aac)
A.aac.update_docked_status(TRUE)