diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index fee3faf17e..315a250ba2 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -255,8 +255,6 @@ Thus, the two variables affect pump operation are set in New(): "You hear ratchet.") deconstruct() - - //CHOMPEdit Start - Adds TGStation keybinds to save our engineers some time. /obj/machinery/atmospherics/binary/pump/AltClick(mob/user) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) @@ -281,3 +279,26 @@ Thus, the two variables affect pump operation are set in New(): else to_chat(user, span_warning("Access denied.")) //CHOMPEdit End + +/obj/machinery/atmospherics/binary/pump/high_power + icon = 'icons/atmos/volume_pump.dmi' + icon_state = "map_off" + construction_type = /obj/item/pipe/directional + pipe_state = "volumepump" + level = 1 + + name = "high power gas pump" + desc = "A pump that moves gas from one place to another. Has double the power rating of the standard gas pump." + + power_rating = 15000 //15000 W ~ 20 HP + +/obj/machinery/atmospherics/binary/pump/high_power/on + use_power = USE_POWER_IDLE + icon_state = "map_on" + +/obj/machinery/atmospherics/binary/pump/high_power/update_icon() + if(!powered()) + icon_state = "off" + else + icon_state = "[use_power ? "on" : "off"]" + diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index e6f7c09d95..eb66f71609 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,21 +1,307 @@ -/obj/machinery/atmospherics/binary/pump/high_power +/* +Every cycle, the pump uses the air in air_in to try and move a specific volume of gas into air_out. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ +#define VOLUME_PUMP_MAX_OUTPUT_PRESSURE 9000 // kpa +#define VOLUME_PUMP_LEAK_AMOUNT 0.04 // About 4% of the volume moved will leak. + +/obj/machinery/atmospherics/binary/volume_pump icon = 'icons/atmos/volume_pump.dmi' icon_state = "map_off" construction_type = /obj/item/pipe/directional pipe_state = "volumepump" level = 1 + var/base_icon = "pump" - name = "high power gas pump" - desc = "A pump that moves gas from one place to another. Has double the power rating of the standard gas pump." + name = "volumetric gas pump" + desc = "A pump that moves gas by volume" - power_rating = 15000 //15000 W ~ 20 HP + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 15000 //15000 W ~ 20.4 HP -/obj/machinery/atmospherics/binary/pump/high_power/on - use_power = USE_POWER_IDLE + var/max_transfer_rate = ATMOS_DEFAULT_VOLUME_PUMP // Ls + var/transfer_rate = 20 // L + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + + var/overclocked = FALSE + var/mutable_appearance/overclock_overlay + +/obj/machinery/atmospherics/binary/volume_pump/Initialize(mapload) + . = ..() + + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/volume_pump/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/binary/pump/on icon_state = "map_on" + use_power = USE_POWER_IDLE -/obj/machinery/atmospherics/binary/pump/high_power/update_icon() +/obj/machinery/atmospherics/binary/volume_pump/fuel + icon_state = "map_off-fuel" + base_icon = "pump-fuel" + icon_connect_type = "-fuel" + connect_types = CONNECT_TYPE_FUEL + +/obj/machinery/atmospherics/binary/volume_pump/fuel/on + icon_state = "map_on-fuel" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/volume_pump/aux + icon_state = "map_off-aux" + base_icon = "pump-aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX + +/obj/machinery/atmospherics/binary/volume_pump/aux/on + icon_state = "map_on-aux" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/volume_pump/update_icon() if(!powered()) icon_state = "off" else icon_state = "[use_power ? "on" : "off"]" + + overclock_overlay = mutable_appearance('icons/atmos/volume_pump_overclock.dmi', "vpumpoverclock") + if(powered() && use_power && overclocked) + add_overlay(overclock_overlay) + else + cut_overlay(overclock_overlay) + +/obj/machinery/atmospherics/binary/volume_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180), node1?.icon_connect_type) + add_underlay(T, node2, dir, node2?.icon_connect_type) + +/obj/machinery/atmospherics/binary/volume_pump/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/binary/volume_pump/process() + last_power_draw = 0 + last_flow_rate = 0 + var/power_draw = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + var/input_starting_moles = air1.total_moles + var/output_starting_pressure = air2.return_pressure() + + // The pump will refuse to do anything if the pressure is too high or low, unless it is overclocked. + if((input_starting_moles < MINIMUM_MOLES_TO_PUMP || output_starting_pressure > VOLUME_PUMP_MAX_OUTPUT_PRESSURE) && !overclocked) + return + + var/transfer_ratio = transfer_rate / air1.volume + if(!transfer_ratio) + return + + var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio) + var/transfer_moles = removed.total_moles + last_flow_rate = (transfer_moles/input_starting_moles)*air1.volume + + // Some gases will leak if overclocked. Trade off for no pump limits. + if(overclocked) + var/datum/gas_mixture/environment = loc.return_air() + var/datum/gas_mixture/leaked = removed.remove_ratio(VOLUME_PUMP_LEAK_AMOUNT) + environment.merge(leaked) + + air2.merge(removed) + + // This part is necessary, as the function pump_gas has limits that reduces the volume pump to just a glorified pressure pump. + // The gas pump does not care about trying to meet a specific pressure. It will keep moving gas till a pressure limit is reached. + power_draw = (transfer_moles/input_starting_moles)*power_rating + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + if(network1) + network1.update = TRUE + + if(network2) + network2.update = TRUE + + return TRUE + +//Radio remote control + +/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status() + if(!radio_connection) + return FALSE + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = use_power, + "transfer_rate" = transfer_rate, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + return TRUE + +/obj/machinery/atmospherics/binary/volume_pump/tgui_interact(mob/user, datum/tgui/ui) + if(stat & (BROKEN|NOPOWER)) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GasPump", name) + ui.open() + +/obj/machinery/atmospherics/binary/volume_pump/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/list/data = list( + "on" = use_power, + "rate" = transfer_rate*100, + "max_rate" = max_transfer_rate, + "last_flow_rate" = last_flow_rate*10, + "last_power_draw" = last_power_draw, + "max_power_draw" = power_rating, + ) + + return data + +/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return FALSE + + if(signal.data["power"]) + if(text2num(signal.data["power"])) + update_use_power(USE_POWER_IDLE) + else + update_use_power(USE_POWER_OFF) + + if("power_toggle" in signal.data) + update_use_power(!use_power) + + if(signal.data["set_volume_rate"]) + transfer_rate = between(0, text2num(signal.data["set_volume_rate"]), air1.volume) + + if(signal.data["status"]) + broadcast_status() + return //do not update_icon + + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user) + if(..()) + return + add_fingerprint(user) + if(!allowed(user)) + to_chat(user, span_warning("Access denied.")) + return + tgui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/tgui_act(action, params, datum/tgui/ui) + if(..()) + return TRUE + + switch(action) + if("power") + update_use_power(!use_power) + . = TRUE + if("set_press") + var/press = params["press"] + switch(press) + if("min") + transfer_rate = 0 + if("max") + transfer_rate = max_transfer_rate + if("set") + var/new_rate = tgui_input_number(ui.user,"Enter new transfer rate (0-[max_transfer_rate] L/s)","Flow Control",src.transfer_rate, max_transfer_rate, 0) + src.transfer_rate = between(0, new_rate, max_transfer_rate) + . = TRUE + + add_fingerprint(ui.user) + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (W.has_tool_quality(TOOL_WRENCH)) + wrench_act(W, user) + + if (W.has_tool_quality(TOOL_MULTITOOL)) + multitool_act(W, user) + +/obj/machinery/atmospherics/binary/volume_pump/examine(mob/user) + . = ..() + . += "This device is designed to move large volumes of gasses quickly, but with no gurantee of exact pressures.\ + Meaning that this can naievely over-pressurize pipes and devices past the device's designed limit." + . += span_bold("The [src]'s pressure limit is [VOLUME_PUMP_MAX_OUTPUT_PRESSURE].") + . += span_notice("Its pressure limits could be [overclocked ? "en" : "dis"]abled with a" + span_bold("multitool") + ".") + if(overclocked) + . += "Its warning light is on[use_power ? " and it's spewing gas!" : "."]" + + +/obj/machinery/atmospherics/binary/volume_pump/proc/wrench_act(var/obj/item/W as obj, var/mob/user as mob) + if (!(stat & NOPOWER) && use_power) + to_chat(user, span_warning("You cannot unwrench this [src], turn it off first.")) + return TRUE + if(!can_unwrench()) + to_chat(user, span_warning("You cannot unwrench this [src], it too exerted due to internal pressure.")) + add_fingerprint(user) + return TRUE + playsound(src, W.usesound, 50, 1) + to_chat(user, span_notice("You begin to unfasten \the [src]...")) + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + span_infoplain(span_bold("\The [user]") + " unfastens \the [src]."), \ + span_notice("You have unfastened \the [src]."), \ + "You hear ratchet.") + deconstruct() + +/obj/machinery/atmospherics/binary/volume_pump/proc/multitool_act(var/obj/item/W as obj, var/mob/user as mob) + if(!overclocked) + overclocked = TRUE + to_chat(user, span_notice("The pump makes a grinding noise and air starts to hiss out as you disable its pressure limits.")) + else + overclocked = FALSE + to_chat(user, span_notice("The pump quiets down as you turn its limiters back on.")) + update_icon() + +#undef VOLUME_PUMP_MAX_OUTPUT_PRESSURE +#undef VOLUME_PUMP_LEAK_AMOUNT diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm index d5c951e88d..57da19c723 100644 --- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm +++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm @@ -162,6 +162,32 @@ update_icon() /obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (W.has_tool_quality(TOOL_MULTITOOL)) + var/list/options = list("Frequency", "ID Tag", "-SAVE TO BUFFER-", "Cancel") + var/answer = tgui_alert(user, "[src] has an ID of \"[id]\" and a frequency of [frequency]. What would you like to change?", "Options!", options) + if(!answer || answer == "Cancel" || !Adjacent(user)) + return + + switch(answer) + if("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + to_chat(user, span_notice("You set the [src]'s frequency to [frequency].")) + + if("ID Tag") + id = tgui_input_text(user, "Please insert an ID tag for [src], example 'exhaust_port'.", "Set ID Tag", id, MAX_NAME_LEN) + if(id) + to_chat(user, span_notice("You set the [src]'s ID Tag to \"[id]\".")) + + if("-SAVE TO BUFFER-") + var/obj/item/multitool/tool = W + tool.connectable = src + to_chat(user, span_notice("You copied the [src] into the [tool]'s buffer!")) + + return ..() + if (!W.has_tool_quality(TOOL_WRENCH)) return ..() @@ -173,3 +199,11 @@ span_notice("You have unfastened \the [src]."), \ "You hear a ratchet.") deconstruct() + +/obj/machinery/atmospherics/unary/outlet_injector/CtrlClick(mob/user) + if (volume_rate == ATMOS_DEFAULT_VOLUME_PUMP + 500 || use_power == USE_POWER_OFF) + return ..() + + volume_rate = ATMOS_DEFAULT_VOLUME_PUMP + 500 + to_chat(user, span_notice("You have set \the [src] to [volume_rate]")) + update_icon() diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 389a3d49f7..decc9ea239 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -298,13 +298,13 @@ /obj/machinery/atmospherics/unary/vent_pump/atmos_init() ..() - //some vents work his own special way - radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null - radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null if(frequency) set_frequency(frequency) /obj/machinery/atmospherics/unary/vent_pump/proc/set_frequency(new_frequency) + //some vents work his own special way + radio_filter_in = new_frequency==1439?(RADIO_FROM_AIRALARM):null + radio_filter_out = new_frequency==1439?(RADIO_TO_AIRALARM):null radio_connection = register_radio(src, frequency, new_frequency, radio_filter_in) frequency = new_frequency broadcast_status() @@ -404,18 +404,7 @@ to_chat(user, span_warning("You need more welding fuel to complete this task.")) return 1 if(W.has_tool_quality(TOOL_MULTITOOL)) - var/choice = tgui_alert(user, "[src] has an ID of \"[id_tag]\" and a frequency of [frequency]. What would you like to change?", "[src] ID", list("ID Tag", "Frequency", "Nothing")) - switch(choice) - if("ID Tag") - var/new_id = tgui_input_text(user, "[src] has an ID of \"[id_tag]\". What would you like it to be?", "[src] ID", id_tag, 30, FALSE, TRUE) - if(new_id) - id_tag = new_id - - if("Frequency") - var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) - if(new_frequency) - new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(new_frequency) + multitool_act(W, user) return TRUE else ..() @@ -458,6 +447,31 @@ "You hear a ratchet.") deconstruct() +/obj/machinery/atmospherics/unary/vent_pump/proc/multitool_act(obj/item/W, mob/user) + var/list/options = list( + "ID Tag", "Frequency", "Direction", "-SAVE TO BUFFER-") + var/choice = tgui_input_list(user, "[src] has an ID of \"[id_tag]\" and a frequency of [frequency]. What would you like to change?", "[src] Config", options) + switch(choice) + if("ID Tag") + var/new_id = tgui_input_text(user, "[src] has an ID of \"[id_tag]\". What would you like it to be?", "[src] ID", id_tag, 30) + if(new_id) + id_tag = new_id + + if("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be? Note, 1439 will only hail Air Alarms for this device.", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + + if("-SAVE TO BUFFER-") + var/obj/item/multitool/tool = W + tool.connectable = src + + if("Direction") + pump_direction = !pump_direction + to_chat(user, span_notice("[src] is now [pump_direction ? "pumping in" : "siphoning out"].")) + update_icon() + #undef DEFAULT_PRESSURE_DELTA #undef EXTERNAL_PRESSURE_BOUND diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm index 2306775ad5..489670fdeb 100644 --- a/code/ATMOSPHERICS/datum_pipe_network.dm +++ b/code/ATMOSPHERICS/datum_pipe_network.dm @@ -8,7 +8,7 @@ var/list/leaks = list() - var/update = 1 + var/update = TRUE //var/datum/gas_mixture/air_transient = null /datum/pipe_network/Destroy() diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index 4fd35cf9e5..bf1c5646b9 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -1,3 +1,11 @@ +#define SENSOR_PRESSURE (1<<0) +#define SENSOR_TEMPERATURE (1<<1) +#define SENSOR_O2 (1<<2) +#define SENSOR_PLASMA (1<<3) +#define SENSOR_N2 (1<<4) +#define SENSOR_CO2 (1<<5) +#define SENSOR_N2O (1<<6) + /obj/machinery/air_sensor icon = 'icons/obj/stationobjs.dmi' icon_state = "gsensor1" @@ -74,6 +82,75 @@ SSradio.remove_object(src,frequency) . = ..() +/obj/machinery/air_sensor/attackby(obj/item/W, mob/user) + if(W.has_tool_quality(TOOL_WRENCH)) + return wrench_act(user, W) + + if(W.has_tool_quality(TOOL_MULTITOOL)) + return multitool_act(user, W) + + return ..() + +/obj/machinery/air_sensor/proc/wrench_act(var/mob/living/user, var/obj/item/tool/wrench/W) + playsound(src, W.usesound, 50, 1) + user.visible_message("[user] unfastens \the [src].", span_notice("You have unfastened \the [src]."), "You hear ratcheting.") + var/obj/item/pipe_gsensor/gsensor = new /obj/item/pipe_gsensor(loc) + gsensor.id_tag = id_tag + gsensor.output = output + qdel(src) + playsound(src, 'sound/items/deconstruct.ogg', 50, 1) + +#define ONOFF_TOGGLE(flag) "\[[(output & flag) ? "YES" : "NO"]]" +/obj/machinery/air_sensor/proc/multitool_act(mob/living/user, obj/item/multitool/tool) + var/list/options = list( + "Pressure: [ONOFF_TOGGLE(SENSOR_PRESSURE)]" = SENSOR_PRESSURE, + "Temperature: [ONOFF_TOGGLE(SENSOR_TEMPERATURE)]" = SENSOR_TEMPERATURE, + "Oxygen: [ONOFF_TOGGLE(SENSOR_O2)]" = SENSOR_O2, + "Toxins: [ONOFF_TOGGLE(SENSOR_PLASMA)]" = SENSOR_PLASMA, + "Nitrogen: [ONOFF_TOGGLE(SENSOR_N2)]" = SENSOR_N2, + "Carbon Dioxide: [ONOFF_TOGGLE(SENSOR_CO2)]" = SENSOR_CO2, + "Nitrous Oxide: [ONOFF_TOGGLE(SENSOR_N2O)]" = SENSOR_N2O, + "-SAVE TO BUFFER-" = "multitool" + ) + + var/answer = tgui_input_list(user, "[src] has an ID of \"[id_tag]\" and a frequency of [frequency]. What would you like to change?", "Options!", options) + + if(!(src in view(5, user))) + return TRUE + + if(answer in options) // Null will break us out + switch(options[answer]) + if(SENSOR_PRESSURE) + output ^= SENSOR_PRESSURE + if(SENSOR_TEMPERATURE) + output ^= SENSOR_TEMPERATURE + if(SENSOR_O2) + output ^= SENSOR_O2 + if(SENSOR_PLASMA) + output ^= SENSOR_PLASMA + if(SENSOR_N2) + output ^= SENSOR_N2 + if(SENSOR_CO2) + output ^= SENSOR_CO2 + if(SENSOR_N2O) + output ^= SENSOR_N2O + if("frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + if("multitool") + id_tag = tgui_input_text(user, "Please insert an ID tag for [src], example 'burn_chamber'.", "Set ID Tag", id_tag, MAX_NAME_LEN, FALSE) + if(!id_tag || !Adjacent(user)) + return + + var/obj/item/multitool/M = tool + M.connectable = src + to_chat(user, span_notice("You save [src] into [M]'s buffer.")) + + return TRUE +#undef ONOFF_TOGGLE + /obj/machinery/computer/general_air_control icon_keyboard = "atmos_key" icon_screen = "tank" @@ -96,6 +173,12 @@ tgui_interact(user) +/obj/machinery/computer/general_air_control/attackby(obj/item/W, mob/user) + if(W.has_tool_quality(TOOL_MULTITOOL)) + return multitool_act(W, user) + + . = ..(W, user) + /obj/machinery/computer/general_air_control/receive_signal(datum/signal/signal) if(!signal || signal.encryption) return @@ -130,6 +213,70 @@ frequency = new_frequency radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) +/obj/machinery/computer/general_air_control/proc/multitool_act(obj/item/W, mob/user) + var/list/options = list("Sensors", "Frequency", "Cancel") + var/answer = tgui_input_list(user, "[src] has a frequency of [frequency]. What would you like to change?", "Options!", options) + . = TRUE + if(!answer || answer == "Cancel" || !Adjacent(user)) + return + + switch(answer) + if("Sensors") + configure_sensors(user, W) + + if("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + + return + +/obj/machinery/computer/general_air_control/proc/configure_sensors(mob/living/user, obj/item/multitool/tool) + to_chat(user, "CONFIGURE SENSOR FUNC") + var/choice = tgui_input_list(user, "Would you like to add or remove a sensor/meter?", "Configuration", list("Add", "Remove","Cancel")) + if( !choice || choice == "Cancel" || !Adjacent(user)) + return + + switch(choice) + if("Add") + // Device must be a meter or gas sensor. + var/obj/machinery/device = tool.connectable + if(!device || !(istype(device, /obj/machinery/meter)) && !(istype(device, /obj/machinery/air_sensor))) + to_chat(user, span_warning("Error: No device in multitool buffer, or incompatible device is not a sensor or meter.")) + return + + var/device_name = tgui_input_text(user, "Enter a name for the Sensor/Meter.", "Name") + if (!device_name || !Adjacent(user)) + to_chat(user, span_warning("Error: No name was given for [tool.connectable].")) + return + + if(istype(device, /obj/machinery/air_sensor)) + var/obj/machinery/air_sensor/AS = device + sensors[AS.id_tag] = device_name + else + var/obj/machinery/meter/M = device + sensors[M.id] = device_name + + to_chat(user, span_notice("You have added the [tool.connectable] to the [src] under the name [device_name]!")) + + if("Remove") + // Creates an associative mapping of Names to Tags, from Tags to Names. + var/list/sensor_names = list() + for(tag in sensors) + sensor_names[sensors[tag]] = sensors[tag] + + var/to_remove = tgui_input_list(user, "Select a sensor/meter to remove", "Sensor/Meter Removal", sensor_names) + if(!to_remove) + return + + var/confirm = tgui_alert(user, "Are you sure you want to remove the sensor/meter '[to_remove]'?", "Warning", list("Yes", "No")) + if(confirm != "Yes" || !Adjacent(user)) + return + + sensors -= sensor_names[to_remove] + to_chat(user, span_notice("Successfully removed sensor/meter with name [to_remove]")) + /obj/machinery/computer/general_air_control/Initialize(mapload) . = ..() if(frequency) @@ -138,6 +285,8 @@ /obj/machinery/computer/general_air_control/large_tank_control icon = 'icons/obj/computer.dmi' frequency = 1441 + name = "Large Tank Computer" + desc = "Controls various devices for managing a gas tank." var/input_tag var/output_tag var/list/input_info @@ -234,6 +383,75 @@ signal.data["sigtype"]="command" radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) +/obj/machinery/computer/general_air_control/large_tank_control/multitool_act(obj/item/W, mob/user) + var/list/options = list("Inlet", "Outlet", "Sensors", "Frequency", "Cancel") + var/choice = tgui_input_list(user, "[src] has a frequency of [frequency]. What would you like to change?", "Configuration", options) + if(!choice || choice == "Cancel" || !Adjacent(user)) + return + + switch(choice) + if ("Inlet") + configure_inlet(user, W) + + if ("Outlet") + configure_outlet(user, W) + + if ("Sensors") + configure_sensors(user, W) + + if ("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + + return TRUE + +/obj/machinery/computer/general_air_control/large_tank_control/proc/configure_outlet(mob/living/user, obj/item/multitool/tool) + var/choice = tgui_alert(user, "Would you like to set an outlet or clear it?", "Configuration", list("Set", "Clear", "Cancel")) + if(!choice || !Adjacent(user) || choice == "Cancel") + return + + switch(choice) + if ("Set") + to_chat(user, span_notice("The buffer is [tool.connectable]")) + if (!istype(tool.connectable, /obj/machinery/atmospherics/unary/vent_pump)) + to_chat(user, span_notice("Error: Buffer is either empty, or object in buffer is invalid. Device should be a Unary Vent.")) + return + + var/obj/machinery/atmospherics/unary/vent_pump/pump = tool.connectable + output_tag = pump.id_tag + pump.external_pressure_bound = 0 + pump.external_pressure_bound_default = 0 + to_chat(user, span_notice("You have set the outlet!")) + return + + if ("Clear") + output_tag = null + to_chat(user, span_notice("You have cleared the outlet!")) + return + +/obj/machinery/computer/general_air_control/large_tank_control/proc/configure_inlet(mob/living/user, obj/item/multitool/tool) + var/choice = tgui_alert(user, "Would you like to set an inlet or clear it?", "Configuration", list("Set", "Clear", "Cancel")) + if(!choice || !Adjacent(user) || choice == "Cancel") + return + + switch(choice) + if ("Set") + if (!istype(tool.connectable, /obj/machinery/atmospherics/unary/outlet_injector)) + to_chat(user, span_notice("Error: Buffer is either empty, or object in buffer is invalid. Device should be Injector")) + return + + var/obj/machinery/atmospherics/unary/outlet_injector/injector = tool.connectable + input_tag = injector.id + to_chat(user, span_notice("You have set the inlet")) + return + + if ("Clear") + input_tag = null + to_chat(user, span_notice("You have cleared the inlet!")) + return + /obj/machinery/computer/general_air_control/supermatter_core icon = 'icons/obj/computer.dmi' frequency = 1433 @@ -335,6 +553,75 @@ signal.data["sigtype"]="command" radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) +/obj/machinery/computer/general_air_control/supermatter_core/multitool_act(obj/item/W, mob/user) + var/list/options = list("Inlet", "Outlet", "Sensors", "Frequency") + var/choice = tgui_input_list(user, "[src] has a frequency of [frequency]. What would you like to change?", "Configuration", options) + if(!choice || choice == "Cancel" || !Adjacent(user)) + return + + switch(choice) + if ("Inlet") + configure_inlet(user, W) + + if ("Outlet") + configure_outlet(user, W) + + if ("Sensors") + configure_sensors(user, W) + + if ("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + + return TRUE + +/obj/machinery/computer/general_air_control/supermatter_core/proc/configure_outlet(mob/living/user, obj/item/multitool/tool) + var/choice = tgui_alert(user, "Would you like to set an outlet or clear it?", "Configuration", list("Set", "Clear", "Cancel")) + if(!choice || !Adjacent(user) || choice == "Cancel") + return + + switch(choice) + if ("Set") + if (!istype(tool.connectable, /obj/machinery/atmospherics/unary/vent_pump)) + to_chat(user, span_warning("Error: Buffer is either empty, or object in buffer is invalid. Device should be Air Vent")) + return + + var/obj/machinery/atmospherics/unary/vent_pump/pump = tool.connectable + output_tag = pump.id_tag + pump.external_pressure_bound = 0 + pump.external_pressure_bound_default = 0 + to_chat(user, span_notice("You have set the outlet!")) + return + + if ("Clear") + output_tag = null + to_chat(user, span_notice("You have cleared the outlet!")) + return + +/obj/machinery/computer/general_air_control/supermatter_core/proc/configure_inlet(mob/living/user, obj/item/multitool/tool) + var/choice = tgui_alert(user, "Would you like to set an inlet or clear it?", "Configuration", list("Set", "Clear", "Cancel")) + if(!choice || !Adjacent(user) || choice == "Cancel") + return + + switch(choice) + if ("Set") + to_chat(user, span_notice("The buffer is [tool.connectable]")) + if (!istype(tool.connectable, /obj/machinery/atmospherics/unary/outlet_injector)) + to_chat(user, span_warning("Error: Buffer is either empty, or object in buffer is invalid. Device should be Injector")) + return + + var/obj/machinery/atmospherics/unary/outlet_injector/injector = tool.connectable + input_tag = injector.id + to_chat(user, span_notice("You have set the inlet!")) + return + + if ("Clear") + input_tag = null + to_chat(user, span_notice("You have cleared the inlet!")) + return + /obj/machinery/computer/general_air_control/fuel_injection icon = 'icons/obj/computer.dmi' icon_screen = "alert:0" @@ -453,3 +740,11 @@ radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) . = TRUE + +#undef SENSOR_PRESSURE +#undef SENSOR_TEMPERATURE +#undef SENSOR_O2 +#undef SENSOR_PLASMA +#undef SENSOR_N2 +#undef SENSOR_CO2 +#undef SENSOR_N2O diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 3f4232f0e7..e96fd1bd10 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -9,6 +9,7 @@ power_channel = ENVIRON var/frequency = 0 var/id + var/open = FALSE use_power = USE_POWER_IDLE idle_power_usage = 15 @@ -117,7 +118,19 @@ qdel(src) return - if(istype(W, /obj/item/multitool)) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, W.usesound, 50, 1) + to_chat(user, span_notice("You have [open ? "closed" : "opened"] the maintenance panel for [src].")) + open = !open + return + + if(W.has_tool_quality(TOOL_MULTITOOL)) + if(open) // For setting up the meter to be used by other devices over radio. + id = tgui_input_text(user, "Please insert an ID tag for [src], example 'exhaust_pipe'.", "Set ID Tag", id, MAX_NAME_LEN) + var/obj/item/multitool/tool = W + tool.connectable = src + return + for(var/obj/machinery/atmospherics/pipe/P in loc) pipes_on_turf |= P if(!pipes_on_turf.len) diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 39a9574ab6..a66a4c4f0c 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -293,3 +293,30 @@ Buildable meters /obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT) piping_layer = new_layer + +/obj/item/pipe_gsensor + name = "gas sensor" + desc = "A sensor that can be hooked to a computer." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "gsensor0" + item_state = "buildpipe" + w_class = ITEMSIZE_LARGE + var/label = null + var/id_tag + var/output = 3 + +/obj/item/pipe_gsensor/attackby(obj/item/W, mob/user) + if(W.has_tool_quality(TOOL_WRENCH)) + return wrench_act(user, W) + return ..() + +/obj/item/pipe_gsensor/proc/wrench_act(mob/living/user, obj/item/tool/wrench/W) + var/obj/machinery/air_sensor/air_sensor = new /obj/machinery/air_sensor(loc) + air_sensor.id_tag = id_tag + air_sensor.output = output + playsound(src, W.usesound, 50, 1) + to_chat(user, span_notice("You fasten the meter to the pipe.")) + qdel(src) + +/obj/item/pipe_gsensor/dropped(mob/user) + . = ..() diff --git a/code/game/machinery/pipe/pipe_recipes.dm b/code/game/machinery/pipe/pipe_recipes.dm index d437cb3670..f301007a59 100644 --- a/code/game/machinery/pipe/pipe_recipes.dm +++ b/code/game/machinery/pipe/pipe_recipes.dm @@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(atmos_pipe_recipes, list( new /datum/pipe_recipe/pipe("Aux Pump", /obj/machinery/atmospherics/binary/pump/aux), new /datum/pipe_recipe/pipe("Pressure Regulator", /obj/machinery/atmospherics/binary/passive_gate), new /datum/pipe_recipe/pipe("High Power Gas Pump", /obj/machinery/atmospherics/binary/pump/high_power), + new /datum/pipe_recipe/pipe("Volumetric Gas Pump", /obj/machinery/atmospherics/binary/volume_pump), new /datum/pipe_recipe/pipe("Automatic Shutoff Valve",/obj/machinery/atmospherics/valve/shutoff), new /datum/pipe_recipe/pipe("Scrubber", /obj/machinery/atmospherics/unary/vent_scrubber), new /datum/pipe_recipe/meter("Meter"), @@ -35,6 +36,7 @@ GLOBAL_LIST_INIT(atmos_pipe_recipes, list( new /datum/pipe_recipe/pipe("Gas Mixer 'T'", /obj/machinery/atmospherics/trinary/mixer/t_mixer), new /datum/pipe_recipe/pipe("Omni Gas Mixer", /obj/machinery/atmospherics/omni/mixer), new /datum/pipe_recipe/pipe("Omni Gas Filter", /obj/machinery/atmospherics/omni/atmos_filter), + new /datum/pipe_recipe/air_sensor("Gas Sensor"), ), "Heat Exchange" = list( new /datum/pipe_recipe/pipe("Pipe", /obj/machinery/atmospherics/pipe/simple/heat_exchanging), @@ -147,6 +149,15 @@ GLOBAL_LIST_INIT(disposal_pipe_recipes, list( /datum/pipe_recipe/meter/New(label) name = label +// +// Subtype for gas sensor +// +/datum/pipe_recipe/air_sensor + pipe_type = /obj/item/pipe_gsensor + +/datum/pipe_recipe/air_sensor/New(label) + name = label + // // Subtype for disposal pipes // diff --git a/code/game/objects/items/weapons/RPD_vr.dm b/code/game/objects/items/weapons/RPD_vr.dm index 35f821d05d..2f22e2eb56 100644 --- a/code/game/objects/items/weapons/RPD_vr.dm +++ b/code/game/objects/items/weapons/RPD_vr.dm @@ -174,7 +174,7 @@ make_pipe_whitelist = typecacheof(list(/obj/structure/lattice, /obj/structure/girder, /obj/item/pipe)) var/can_make_pipe = (isturf(A) || is_type_in_typecache(A, make_pipe_whitelist)) - var/can_destroy_pipe = istype(A, /obj/item/pipe) || istype(A, /obj/item/pipe_meter) || istype(A, /obj/structure/disposalconstruct) + var/can_destroy_pipe = istype(A, /obj/item/pipe) || istype(A, /obj/item/pipe_meter) || istype(A, /obj/structure/disposalconstruct) || istype(A, /obj/item/pipe_gsensor) . = TRUE if((mode & DESTROY_MODE) && can_destroy_pipe) @@ -207,6 +207,13 @@ PM.setAttachLayer(queued_piping_layer) if(mode & WRENCH_MODE) do_wrench(PM, user) + else if(istype(recipe, /datum/pipe_recipe/air_sensor)) + to_chat(user, span_notice("You start building an air sensor...")) + if(do_after(user, 2, target = A)) + activate() + var/obj/item/pipe_gsensor/GS = new /obj/item/pipe_gsensor(get_turf(A)) + if(mode & WRENCH_MODE) + do_wrench(GS, user) else if(istype(recipe, /datum/pipe_recipe/pipe)) var/datum/pipe_recipe/pipe/R = recipe to_chat(user, span_notice("You start building a pipe...")) diff --git a/code/modules/research/tg/designs/boards/atmos.dm b/code/modules/research/tg/designs/boards/atmos.dm index 4010aaed71..c13dfba9aa 100644 --- a/code/modules/research/tg/designs/boards/atmos.dm +++ b/code/modules/research/tg/designs/boards/atmos.dm @@ -16,6 +16,15 @@ ) departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_SCIENCE +/datum/design_techweb/board/tank_management + name = "tank monitoring console circuit" + id = "tank_management" + build_path = /obj/item/circuitboard/air_management/tank_control + category = list( + RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_ENGINEERING + ) + departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_SCIENCE + /datum/design_techweb/board/shutoff_monitor name = "Automatic shutoff valve monitor circuit" id = "shutoff_monitor" diff --git a/code/modules/research/tg/techwebs/nodes/atmos_nodes.dm b/code/modules/research/tg/techwebs/nodes/atmos_nodes.dm index dd0d63224a..ee4d5c8663 100644 --- a/code/modules/research/tg/techwebs/nodes/atmos_nodes.dm +++ b/code/modules/research/tg/techwebs/nodes/atmos_nodes.dm @@ -8,6 +8,7 @@ "atmosanalyzer", "atmosalerts", "air_management", + "tank_management", "shutoff_monitor", // "thermomachine", // "space_heater", diff --git a/icons/atmos/volume_pump_overclock.dmi b/icons/atmos/volume_pump_overclock.dmi new file mode 100644 index 0000000000..4770c2ec54 Binary files /dev/null and b/icons/atmos/volume_pump_overclock.dmi differ diff --git a/tgui/packages/tgui/interfaces/GasPump.tsx b/tgui/packages/tgui/interfaces/GasPump.tsx index d6e139854b..764be2d398 100644 --- a/tgui/packages/tgui/interfaces/GasPump.tsx +++ b/tgui/packages/tgui/interfaces/GasPump.tsx @@ -12,6 +12,8 @@ import type { BooleanLike } from 'tgui-core/react'; type Data = { on: BooleanLike; + max_rate: number; + rate: number; pressure_set: number; last_flow_rate: number; last_power_draw: number; @@ -21,8 +23,15 @@ type Data = { export const GasPump = (props) => { const { act, data } = useBackend(); - const { on, pressure_set, last_flow_rate, last_power_draw, max_power_draw } = - data; + const { + on, + max_rate, + rate, + pressure_set, + last_flow_rate, + last_power_draw, + max_power_draw, + } = data; return ( @@ -78,9 +87,15 @@ export const GasPump = (props) => { SET - - {(pressure_set / 100).toFixed(2)} kPa - + {max_rate ? ( + + {`${(rate / 100).toFixed(2)} L/s`} + + ) : ( + + {`${(pressure_set / 100).toFixed(2)} kPa`} + + )}