From 4369049809ff5b0b3cc1ee68e9f279befd4dce2d Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Wed, 8 Jun 2022 02:43:09 +0200 Subject: [PATCH] [MIRROR] Massive plumbing layer/placement improvements [MDB IGNORE] (#14172) * Massive plumbing layer/placement improvements (#66602) * Massive duct improvements * last minute fixes/additions to plumbing layer fixes * letter, loop, and early return fixes * early continues * color comments * reaction chamber colors * rcd tweaks * Update code/datums/components/plumbing/reaction_chamber.dm * Update code/datums/components/plumbing/reaction_chamber.dm * Update code/datums/components/plumbing/_plumbing.dm * Update code/datums/components/plumbing/_plumbing.dm * remove unused var, better duct restacking Co-authored-by: ShizCalev * Massive plumbing layer/placement improvements Co-authored-by: Tastyfish Co-authored-by: ShizCalev --- code/__DEFINES/layers.dm | 1 + code/__DEFINES/plumbing.dm | 26 ++ code/datums/components/plumbing/_plumbing.dm | 164 ++++++------ .../components/plumbing/reaction_chamber.dm | 4 +- code/game/objects/items/RCD.dm | 244 +++++++++++++----- .../objects/structures/lavaland/geyser.dm | 7 +- code/modules/plumbing/ducts.dm | 236 +++++++++-------- .../plumbing/plumbers/_plumb_machinery.dm | 3 +- icons/hud/radial.dmi | Bin 27600 -> 27711 bytes icons/obj/plumbing/connects.dmi | Bin 1525 -> 2387 bytes icons/obj/plumbing/fluid_ducts.dmi | Bin 973 -> 900 bytes 11 files changed, 417 insertions(+), 268 deletions(-) diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 21273ed2273..196a8cb1fd3 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -58,6 +58,7 @@ #define GAS_PIPE_VISIBLE_LAYER 2.47 //layer = initial(layer) + piping_layer / 1000 in atmospherics/update_icon() to determine order of pipe overlap #define GAS_FILTER_LAYER 2.48 #define GAS_PUMP_LAYER 2.49 +#define PLUMBING_PIPE_VISIBILE_LAYER 2.495//layer = initial(layer) + ducting_layer / 3333 in atmospherics/handle_layer() to determine order of duct overlap #define LOW_OBJ_LAYER 2.5 ///catwalk overlay of /turf/open/floor/catwalk_floor #define CATWALK_LAYER 2.51 diff --git a/code/__DEFINES/plumbing.dm b/code/__DEFINES/plumbing.dm index 9666a5be1e1..320bbe8258f 100644 --- a/code/__DEFINES/plumbing.dm +++ b/code/__DEFINES/plumbing.dm @@ -7,3 +7,29 @@ #define DUCT_LAYER_DEFAULT THIRD_DUCT_LAYER #define MACHINE_REAGENT_TRANSFER 10 //the default max plumbing machinery transfers + +/// List of plumbing layers as name => bitflag +GLOBAL_LIST_INIT(plumbing_layers, list( + "First Layer" = FIRST_DUCT_LAYER, + "Second Layer" = SECOND_DUCT_LAYER, + "Default Layer" = THIRD_DUCT_LAYER, + "Fourth Layer" = FOURTH_DUCT_LAYER, + "Fifth Layer" = FIFTH_DUCT_LAYER, +)) + +/// Reverse of plumbing_layers, as "[bitflag]" => name +GLOBAL_LIST_INIT(plumbing_layer_names, list( + "[FIRST_DUCT_LAYER]" = "First Layer", + "[SECOND_DUCT_LAYER]" = "Second Layer", + "[THIRD_DUCT_LAYER]" = "Default Layer", + "[FOURTH_DUCT_LAYER]" = "Fourth Layer", + "[FIFTH_DUCT_LAYER]" = "Fifth Layer", +)) + +/// Name of omni color +#define DUCT_COLOR_OMNI "omni" + +/// Cached radial menu options for plumbing RCD color picker +GLOBAL_LIST_EMPTY(plumbing_color_menu_options) +/// Cached radial menu options for plumbing RCD layer picker +GLOBAL_LIST_EMPTY(plumbing_layer_menu_options) diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm index 47e4f580732..c3c82775aa8 100644 --- a/code/datums/components/plumbing/_plumbing.dm +++ b/code/datums/components/plumbing/_plumbing.dm @@ -22,10 +22,10 @@ var/recipient_reagents_holder ///How do we apply the new reagents to the receiver? Generally doesn't matter, but some stuff, like people, does care if its injected or whatevs var/methods - ///What color is our demand connect? Also it's not auto-colored so you'll have to make new sprites if its anything other than red, blue, yellow or green - var/demand_color = "red" - ///What color is our supply connect? Also, refrain from pointlessly using non-standard colors unless it's really funny or something - var/supply_color = "blue" + ///What color is our demand connect? + var/demand_color = COLOR_RED + ///What color is our supply connect? + var/supply_color = COLOR_BLUE ///turn_connects is for wheter or not we spin with the object to change our pipes /datum/component/plumbing/Initialize(start=TRUE, _ducting_layer, _turn_connects=TRUE, datum/reagents/custom_receiver) @@ -35,14 +35,14 @@ if(_ducting_layer) ducting_layer = _ducting_layer - var/atom/movable/AM = parent - if(!AM.reagents && !custom_receiver) + var/atom/movable/parent_movable = parent + if(!parent_movable.reagents && !custom_receiver) return COMPONENT_INCOMPATIBLE - reagents = AM.reagents + reagents = parent_movable.reagents turn_connects = _turn_connects - set_recipient_reagents_holder(custom_receiver ? custom_receiver : AM.reagents) + set_recipient_reagents_holder(custom_receiver ? custom_receiver : parent_movable.reagents) if(start) //We're registering here because I need to check whether we start active or not, and this is just easier @@ -76,10 +76,10 @@ send_request(D) ///Can we be added to the ductnet? -/datum/component/plumbing/proc/can_add(datum/ductnet/D, dir) +/datum/component/plumbing/proc/can_add(datum/ductnet/ductnet, dir) if(!active) return - if(!dir || !D) + if(!dir || !ductnet) return FALSE if(num2text(dir) in ducts) return FALSE @@ -97,15 +97,13 @@ if(!ducts.Find(num2text(dir))) return net = ducts[num2text(dir)] - for(var/A in net.suppliers) - var/datum/component/plumbing/supplier = A + for(var/datum/component/plumbing/supplier as anything in net.suppliers) if(supplier.can_give(amount, reagent, net)) valid_suppliers += supplier // Need to ask for each in turn very carefully, making sure we get the total volume. This is to avoid a division that would always round down and become 0 var/targetVolume = reagents.total_volume + amount var/suppliersLeft = valid_suppliers.len - for(var/A in valid_suppliers) - var/datum/component/plumbing/give = A + for(var/datum/component/plumbing/give as anything in valid_suppliers) var/currentRequest = (targetVolume - reagents.total_volume) / suppliersLeft give.transfer_to(src, currentRequest, reagent, net) suppliersLeft-- @@ -116,9 +114,8 @@ return if(reagent) //only asked for one type of reagent - for(var/A in reagents.reagent_list) - var/datum/reagent/R = A - if(R.type == reagent) + for(var/datum/reagent/contained_reagent as anything in reagents.reagent_list) + if(contained_reagent.type == reagent) return TRUE else if(reagents.total_volume > 0) //take whatever return TRUE @@ -133,7 +130,7 @@ reagents.trans_to(target.recipient_reagents_holder, amount, round_robin = TRUE, methods = methods)//we deal with alot of precise calculations so we round_robin=TRUE. Otherwise we get floating point errors, 1 != 1 and 2.5 + 2.5 = 6 ///We create our luxurious piping overlays/underlays, to indicate where we do what. only called once if use_overlays = TRUE in Initialize() -/datum/component/plumbing/proc/create_overlays(atom/movable/AM, list/overlays) +/datum/component/plumbing/proc/create_overlays(atom/movable/parent_movable, list/overlays) SIGNAL_HANDLER if(tile_covered || !use_overlays) @@ -158,39 +155,30 @@ var/duct_y = offset - for(var/D in GLOB.cardinals) + for(var/direction in GLOB.cardinals) var/color - var/direction - if(D & initial(demand_connects)) + if(direction & initial(demand_connects)) color = demand_color - else if(D & initial(supply_connects)) + else if(direction & initial(supply_connects)) color = supply_color else continue - var/image/I - - switch(D) - if(NORTH) - direction = "north" - if(SOUTH) - direction = "south" - if(EAST) - direction = "east" - if(WEST) - direction = "west" + var/direction_text = dir2text(direction) + var/duct_layer = PLUMBING_PIPE_VISIBILE_LAYER + ducting_layer * 0.0003 + var/image/overlay if(turn_connects) - I = image('icons/obj/plumbing/connects.dmi', "[direction]-[color]", layer = AM.layer - 1) - + overlay = image('icons/obj/plumbing/connects.dmi', "[direction_text]-[ducting_layer]", layer = duct_layer) else - I = image('icons/obj/plumbing/connects.dmi', "[direction]-[color]-s", layer = AM.layer - 1) //color is not color as in the var, it's just the name of the icon_state - I.dir = D + overlay = image('icons/obj/plumbing/connects.dmi', "[direction_text]-[ducting_layer]-s", layer = duct_layer) + overlay.dir = direction - I.pixel_x = duct_x - I.pixel_y = duct_y + overlay.color = color + overlay.pixel_x = duct_x + overlay.pixel_y = duct_y - overlays += I + overlays += overlay ///we stop acting like a plumbing thing and disconnect if we are, so we can safely be moved and stuff /datum/component/plumbing/proc/disable() @@ -201,19 +189,21 @@ STOP_PROCESSING(SSplumbing, src) - for(var/A in ducts) - var/datum/ductnet/D = ducts[A] - D.remove_plumber(src) + for(var/duct_dir in ducts) + var/datum/ductnet/duct = ducts[duct_dir] + duct.remove_plumber(src) active = FALSE - for(var/D in GLOB.cardinals) - if(D & (demand_connects | supply_connects)) - for(var/obj/machinery/duct/duct in get_step(parent, D)) - if(duct.duct_layer == ducting_layer) - duct.remove_connects(turn(D, 180)) - duct.neighbours.Remove(parent) - duct.update_appearance() + for(var/direction in GLOB.cardinals) + if(!(direction & (demand_connects | supply_connects))) + continue + for(var/obj/machinery/duct/duct in get_step(parent, direction)) + if(!(duct.duct_layer & ducting_layer)) + continue + duct.remove_connects(turn(direction, 180)) + duct.neighbours.Remove(parent) + duct.update_appearance() ///settle wherever we are, and start behaving like a piece of plumbing /datum/component/plumbing/proc/enable(obj/object, datum/component/component) @@ -225,29 +215,32 @@ update_dir() active = TRUE - var/atom/movable/AM = parent - for(var/obj/machinery/duct/D in AM.loc) //Destroy any ducts under us. Ducts also self-destruct if placed under a plumbing machine. machines disable when they get moved - if(D.anchored) //that should cover everything - D.disconnect_duct() + var/atom/movable/parent_movable = parent + // Destroy any ducts under us on the same layer. + // Ducts also self-destruct if placed under a plumbing machine. + // Machines disable when they get moved + for(var/obj/machinery/duct/duct in parent_movable.loc) + if(duct.anchored && (duct.duct_layer & ducting_layer)) + duct.disconnect_duct() if(demand_connects) START_PROCESSING(SSplumbing, src) - for(var/D in GLOB.cardinals) + for(var/direction in GLOB.cardinals) + if(!(direction & (demand_connects | supply_connects))) + continue + for(var/atom/movable/found_atom in get_step(parent, direction)) + if(istype(found_atom, /obj/machinery/duct)) + var/obj/machinery/duct/duct = found_atom + duct.attempt_connect() + continue - if(D & (demand_connects | supply_connects)) - for(var/atom/movable/A in get_step(parent, D)) - - if(istype(A, /obj/machinery/duct)) - var/obj/machinery/duct/duct = A - duct.attempt_connect() - else - for(var/datum/component/plumbing/plumber as anything in A.GetComponents(/datum/component/plumbing)) - if(plumber.ducting_layer == ducting_layer) - direct_connect(plumber, D) + for(var/datum/component/plumbing/plumber as anything in found_atom.GetComponents(/datum/component/plumbing)) + if(plumber.ducting_layer & ducting_layer) + direct_connect(plumber, direction) /// Toggle our machinery on or off. This is called by a hook from default_unfasten_wrench with anchored as only param, so we dont have to copypaste this on every object that can move -/datum/component/plumbing/proc/toggle_active(obj/O, new_state) +/datum/component/plumbing/proc/toggle_active(obj/parent_obj, new_state) SIGNAL_HANDLER if(new_state) enable() @@ -271,45 +264,44 @@ demand_connects = initial(demand_connects) supply_connects = initial(supply_connects) else - for(var/D in GLOB.cardinals) - if(D & initial(demand_connects)) - new_demand_connects += turn(D, angle) - if(D & initial(supply_connects)) - new_supply_connects += turn(D, angle) + for(var/direction in GLOB.cardinals) + if(direction & initial(demand_connects)) + new_demand_connects += turn(direction, angle) + if(direction & initial(supply_connects)) + new_supply_connects += turn(direction, angle) demand_connects = new_demand_connects supply_connects = new_supply_connects ///Give the direction of a pipe, and it'll return wich direction it originally was when it's object pointed SOUTH /datum/component/plumbing/proc/get_original_direction(dir) - var/atom/movable/AM = parent - return turn(dir, dir2angle(AM.dir) - 180) + var/atom/movable/parent_movable = parent + return turn(dir, dir2angle(parent_movable.dir) - 180) //special case in-case we want to connect directly with another machine without a duct -/datum/component/plumbing/proc/direct_connect(datum/component/plumbing/P, dir) - if(!P.active) +/datum/component/plumbing/proc/direct_connect(datum/component/plumbing/plumbing, dir) + if(!plumbing.active) return var/opposite_dir = turn(dir, 180) - if(P.demand_connects & opposite_dir && supply_connects & dir || P.supply_connects & opposite_dir && demand_connects & dir) //make sure we arent connecting two supplies or demands + if(plumbing.demand_connects & opposite_dir && supply_connects & dir || plumbing.supply_connects & opposite_dir && demand_connects & dir) //make sure we arent connecting two supplies or demands var/datum/ductnet/net = new() net.add_plumber(src, dir) - net.add_plumber(P, opposite_dir) + net.add_plumber(plumbing, opposite_dir) -/datum/component/plumbing/proc/hide(atom/movable/AM, should_hide) +/datum/component/plumbing/proc/hide(atom/movable/parent_obj, should_hide) SIGNAL_HANDLER tile_covered = should_hide - AM.update_appearance() + parent_obj.update_appearance() -/datum/component/plumbing/proc/change_ducting_layer(obj/caller, obj/O, new_layer = DUCT_LAYER_DEFAULT) +/datum/component/plumbing/proc/change_ducting_layer(obj/caller, obj/changer, new_layer = DUCT_LAYER_DEFAULT) SIGNAL_HANDLER ducting_layer = new_layer - if(ismovable(parent)) - var/atom/movable/AM = parent - AM.update_appearance() + var/atom/movable/parent_movable = parent + parent_movable.update_appearance() - if(O) - playsound(O, 'sound/items/ratchet.ogg', 10, TRUE) //sound + if(changer) + playsound(changer, 'sound/items/ratchet.ogg', 10, TRUE) //sound //quickly disconnect and reconnect the network. if(active) @@ -348,7 +340,7 @@ demand_connects = NORTH supply_connects = SOUTH -/datum/component/plumbing/manifold/change_ducting_layer(obj/caller, obj/O, new_layer) +/datum/component/plumbing/manifold/change_ducting_layer(obj/caller, obj/changer, new_layer) return #define READY 2 diff --git a/code/datums/components/plumbing/reaction_chamber.dm b/code/datums/components/plumbing/reaction_chamber.dm index fe6064cccc8..c750fda7142 100644 --- a/code/datums/components/plumbing/reaction_chamber.dm +++ b/code/datums/components/plumbing/reaction_chamber.dm @@ -40,7 +40,7 @@ ///Special connect that we currently use for reaction chambers. Being used so we can keep certain inputs separate, like into a special internal acid container /datum/component/plumbing/acidic_input demand_connects = WEST - demand_color = "yellow" + demand_color = COLOR_YELLOW ducting_layer = SECOND_DUCT_LAYER @@ -50,7 +50,7 @@ ///Special connect that we currently use for reaction chambers. Being used so we can keep certain inputs separate, like into a special internal base container /datum/component/plumbing/alkaline_input demand_connects = EAST - demand_color = "green" + demand_color = COLOR_VIBRANT_LIME ducting_layer = FOURTH_DUCT_LAYER diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index 582feb952e8..6574224f645 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -87,7 +87,7 @@ RLD upgrade |= rcd_up.upgrade if((rcd_up.upgrade & RCD_UPGRADE_SILO_LINK) && !silo_mats) silo_mats = AddComponent(/datum/component/remote_materials, "RCD", FALSE, FALSE) - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) qdel(rcd_up) /// Inserts matter into the RCD allowing it to build @@ -105,7 +105,7 @@ RLD if(R.ammoamt <= 0) qdel(R) matter += load - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) loaded = TRUE else if(istype(O, /obj/item/stack)) loaded = loadwithsheets(O, user) @@ -124,17 +124,17 @@ RLD var/amount_to_use = min(S.amount, maxsheets) S.use(amount_to_use) matter += value*amount_to_use - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) to_chat(user, span_notice("You insert [amount_to_use] [S.name] sheets into [src]. ")) return TRUE to_chat(user, span_warning("You can't insert any more [S.name] sheets into [src]!")) return FALSE /obj/item/construction/proc/activate() - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE) + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) /obj/item/construction/attack_self(mob/user) - playsound(src.loc, 'sound/effects/pop.ogg', 50, FALSE) + playsound(loc, 'sound/effects/pop.ogg', 50, FALSE) if(prob(20)) spark_system.start() @@ -358,7 +358,7 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) T.rcd_act(user, src, RCD_FLOORWALL) useResource(16, user) activate() - playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + playsound(loc, 'sound/machines/click.ogg', 50, 1) user.gib() return MANUAL_SUICIDE @@ -709,7 +709,7 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) if(rcd_results["mode"] == RCD_MACHINE || rcd_results["mode"] == RCD_COMPUTER || rcd_results["mode"] == RCD_FURNISHING) var/turf/target_turf = get_turf(A) if(target_turf.is_blocked_turf(exclude_mobs = TRUE)) - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) qdel(rcd_effect) return FALSE if(!do_after(user, delay, target = A)) @@ -724,7 +724,7 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) rcd_effect.end_animation() useResource(rcd_results["cost"], user) activate() - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) return TRUE /obj/item/construction/rcd/Initialize(mapload) @@ -1046,7 +1046,7 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) if(checkResource(deconcost, user)) to_chat(user, span_notice("You start deconstructing [A]...")) user.Beam(A,icon_state="light_beam", time = 15) - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) if(do_after(user, decondelay, target = A)) if(!useResource(deconcost, user)) return FALSE @@ -1060,8 +1060,8 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) if(checkResource(floorcost, user)) to_chat(user, span_notice("You start building a wall light...")) user.Beam(A,icon_state="light_beam", time = 15) - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) - playsound(src.loc, 'sound/effects/light_flicker.ogg', 50, FALSE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/effects/light_flicker.ogg', 50, FALSE) if(do_after(user, floordelay, target = A)) if(!istype(W)) return FALSE @@ -1075,7 +1075,7 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) candidates += C if(!candidates.len) to_chat(user, span_warning("Valid target not found...")) - playsound(src.loc, 'sound/misc/compiler-failure.ogg', 30, TRUE) + playsound(loc, 'sound/misc/compiler-failure.ogg', 30, TRUE) return FALSE for(var/turf/open/O in candidates) if(istype(O)) @@ -1106,8 +1106,8 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) if(checkResource(floorcost, user)) to_chat(user, span_notice("You start building a floor light...")) user.Beam(A,icon_state="light_beam", time = 15) - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) - playsound(src.loc, 'sound/effects/light_flicker.ogg', 50, TRUE) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + playsound(loc, 'sound/effects/light_flicker.ogg', 50, TRUE) if(do_after(user, floordelay, target = A)) if(!istype(F)) return FALSE @@ -1170,44 +1170,69 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) var/list/machinery_data = list("cost" = list()) ///This list that holds all the plumbing design types the plumberer can construct. Its purpose is to make it easy to make new plumberer subtypes with a different selection of machines. var/list/plumbing_design_types - ///Possible layers to pick from - var/static/list/layers = list("Second Layer" = SECOND_DUCT_LAYER, "Default Layer" = DUCT_LAYER_DEFAULT, "Fourth Layer" = FOURTH_DUCT_LAYER) ///Current selected layer var/current_layer = "Default Layer" + ///Current selected color, for ducts + var/current_color = "omni" /obj/item/construction/plumbing/Initialize(mapload) . = ..() set_plumbing_designs() +/obj/item/construction/plumbing/examine(mob/user) + . = ..() + . += span_notice("Alt-Click to change layer and duct color.") + +/obj/item/construction/plumbing/equipped(mob/user, slot, initial) + . = ..() + if(slot == ITEM_SLOT_HANDS) + RegisterSignal(user, COMSIG_MOUSE_SCROLL_ON, .proc/mouse_wheeled) + else + UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON) + +/obj/item/construction/plumbing/dropped(mob/user, silent) + UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON) + return ..() + +/obj/item/construction/plumbing/cyborg_unequip(mob/user) + UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON) + return ..() + /obj/item/construction/plumbing/attack_self(mob/user) ..() if(!choices.len) - for(var/A in plumbing_design_types) - var/obj/machinery/plumbing/M = A + for(var/obj/machinery/plumbing/plumbing_type as anything in plumbing_design_types) + choices += list(initial(plumbing_type.name) = image(initial(plumbing_type.icon), icon_state = initial(plumbing_type.icon_state))) + name_to_type[initial(plumbing_type.name)] = plumbing_type + machinery_data["cost"][plumbing_type] = plumbing_design_types[plumbing_type] - choices += list(initial(M.name) = image(icon = initial(M.icon), icon_state = initial(M.icon_state))) - name_to_type[initial(M.name)] = M - machinery_data["cost"][A] = plumbing_design_types[A] + // Update duct icon + var/image/duct_image = choices["fluid duct"] + duct_image.color = current_color var/choice = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) - if(!check_menu(user)) + if(!choice || !check_menu(user)) return blueprint = name_to_type[choice] playsound(src, 'sound/effects/pop.ogg', 50, FALSE) to_chat(user, span_notice("You change [name]s blueprint to '[choice]'.")) -///Set the list of designs this plumbing rcd can make +/** + * Set the list of designs this plumbing rcd can make + */ /obj/item/construction/plumbing/proc/set_plumbing_designs() plumbing_design_types = list( + // Note that the list MUST include fluid ducts. + /obj/machinery/duct = 1, /obj/machinery/plumbing/input = 5, /obj/machinery/plumbing/output = 5, /obj/machinery/plumbing/tank = 20, /obj/machinery/plumbing/synthesizer = 15, /obj/machinery/plumbing/reaction_chamber = 15, /obj/machinery/plumbing/buffer = 10, - /obj/machinery/plumbing/layer_manifold = 5, //Above are the most common machinery which is shown on the first cycle. Keep new additions below THIS line, unless they're probably gonna be needed alot + /obj/machinery/plumbing/layer_manifold = 5, /obj/machinery/plumbing/pill_press = 20, /obj/machinery/plumbing/acclimator = 10, /obj/machinery/plumbing/bottler = 50, @@ -1223,56 +1248,152 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) ) ///pretty much rcd_create, but named differently to make myself feel less bad for copypasting from a sibling-type -/obj/item/construction/plumbing/proc/create_machine(atom/A, mob/user) - if(!machinery_data || !isopenturf(A)) +/obj/item/construction/plumbing/proc/create_machine(atom/destination, mob/user) + if(!machinery_data || !isopenturf(destination)) return FALSE + if(!canPlace(destination)) + var/obj/blueprint_type = blueprint + to_chat(user, span_notice("There is something blocking you from placing a [initial(blueprint_type.name)] there.")) + return if(checkResource(machinery_data["cost"][blueprint], user) && blueprint) //"cost" is relative to delay at a rate of 10 matter/second (1matter/decisecond) rather than playing with 2 different variables since everyone set it to this rate anyways. - if(do_after(user, machinery_data["cost"][blueprint], target = A)) - if(checkResource(machinery_data["cost"][blueprint], user) && canPlace(A)) + if(do_after(user, machinery_data["cost"][blueprint], target = destination)) + if(checkResource(machinery_data["cost"][blueprint], user) && canPlace(destination)) useResource(machinery_data["cost"][blueprint], user) activate() - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) - new blueprint (A, FALSE, layers[current_layer]) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + if(ispath(blueprint, /obj/machinery/duct)) + var/is_omni = current_color == DUCT_COLOR_OMNI + new blueprint(destination, FALSE, GLOB.pipe_paint_colors[current_color], GLOB.plumbing_layers[current_layer], null, is_omni) + else + new blueprint(destination, FALSE, GLOB.plumbing_layers[current_layer]) return TRUE -/obj/item/construction/plumbing/proc/canPlace(turf/T) - if(!isopenturf(T)) +/obj/item/construction/plumbing/proc/canPlace(turf/destination) + if(!isopenturf(destination)) return FALSE . = TRUE - for(var/obj/O in T.contents) - if(O.density) //let's not built ontop of dense stuff, like big machines and other obstacles, it kills my immershion + + var/obj/blueprint_template = blueprint + var/layer_id = GLOB.plumbing_layers[current_layer] + + for(var/obj/content_obj in destination.contents) + // Let's not built ontop of dense stuff, if this is also dense. + if(initial(blueprint_template.density) && content_obj.density) return FALSE -/obj/item/construction/plumbing/afterattack(atom/A, mob/user, proximity) + // Ducts can overlap other plumbing objects IF the layers are different + + // make sure plumbling isn't overlapping. + for(var/datum/component/plumbing/plumber as anything in content_obj.GetComponents(/datum/component/plumbing)) + if(plumber.ducting_layer & layer_id) + return FALSE + + if(istype(content_obj, /obj/machinery/duct)) + // Make sure ducts aren't overlapping. + var/obj/machinery/duct/duct_machine = content_obj + if(duct_machine.duct_layer & layer_id) + return FALSE + +/obj/item/construction/plumbing/afterattack(atom/target, mob/user, proximity) . = ..() if(!prox_check(proximity)) return - if(istype(A, /obj/machinery/plumbing)) - var/obj/machinery/plumbing/P = A - if(P.anchored) - to_chat(user, span_warning("The [P.name] needs to be unanchored!")) + if(istype(target, /obj/machinery/plumbing)) + var/obj/machinery/machine_target = target + if(machine_target.anchored) + to_chat(user, span_warning("The [target.name] needs to be unanchored!")) return - if(do_after(user, 20, target = P)) - P.deconstruct() //Let's not substract matter + if(do_after(user, 20, target = target)) + machine_target.deconstruct() //Let's not substract matter playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) //this is just such a great sound effect else - create_machine(A, user) + create_machine(target, user) /obj/item/construction/plumbing/AltClick(mob/user) - if(!istype(user) || !user.canUseTopic(src, BE_CLOSE)) + // give a menu to pick layers or colors + var/list/options_menu = list( + "Current Layer" = image('icons/hud/radial.dmi', icon_state = "plumbing_layer[GLOB.plumbing_layers[current_layer]]"), + "Current Color" = image('icons/hud/radial.dmi', icon_state = current_color), + ) + + playsound(loc, 'sound/effects/pop.ogg', 50, FALSE) + var/choice = show_radial_menu(user, src, options_menu, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + if(!check_menu(user)) return - //this is just cycling options through a list - var/current_loc = layers.Find(current_layer) + 1 + switch(choice) + if("Current Layer") + choose_layer_menu(user) + if("Current Color") + choose_color_menu(user) - if(current_loc > layers.len) - current_loc = 1 +/** + * Choose the current layer via radial menu. + * + * Arguments: + * * user - current user. + */ +/obj/item/construction/plumbing/proc/choose_layer_menu(mob/user) + if(!GLOB.plumbing_layer_menu_options.len) + for(var/layer_name in GLOB.plumbing_layers) + GLOB.plumbing_layer_menu_options += list((layer_name) = image('icons/hud/radial.dmi', icon_state = "plumbing_layer[GLOB.plumbing_layers[layer_name]]")) - //We want the key (the define), not the index (the string) - current_layer = layers[current_loc] - to_chat(user, span_notice("You switch [src] to [current_layer].")) + playsound(loc, 'sound/effects/pop.ogg', 50, FALSE) + var/new_layer = show_radial_menu(user, src, GLOB.plumbing_layer_menu_options, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + if(!new_layer || !check_menu(user)) + return + + current_layer = new_layer + to_chat(user, span_notice("You set the layer to [new_layer].")) + +/** + * Choose the current color via radial menu. + * + * Arguments: + * * user - current user. + */ +/obj/item/construction/plumbing/proc/choose_color_menu(mob/user) + if(!GLOB.plumbing_color_menu_options.len) + for(var/color_name in GLOB.pipe_paint_colors) + GLOB.plumbing_color_menu_options += list((color_name) = image('icons/hud/radial.dmi', icon_state = color_name)) + + playsound(loc, 'sound/effects/pop.ogg', 50, FALSE) + var/new_color = show_radial_menu(user, src, GLOB.plumbing_color_menu_options, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE) + if(!new_color || !check_menu(user)) + return + + current_color = new_color + to_chat(user, span_notice("You set the color to [new_color].")) + +/** + * Choose layer via mouse wheel, like an RPD + * + * Arguments: + * * source - the user + * * A - the atom being selected, unused. + * * delta_x - X scroll delta + * * delta_y - Y scroll delta + */ +/obj/item/construction/plumbing/proc/mouse_wheeled(mob/source, atom/A, delta_x, delta_y, params) + SIGNAL_HANDLER + if(source.incapacitated(IGNORE_RESTRAINTS|IGNORE_STASIS)) + return + if(delta_y == 0) + return + + if(delta_y < 0) + var/current_loc = GLOB.plumbing_layers.Find(current_layer) + 1 + if(current_loc > GLOB.plumbing_layers.len) + current_loc = 1 + current_layer = GLOB.plumbing_layers[current_loc] + else + var/current_loc = GLOB.plumbing_layers.Find(current_layer) - 1 + if(current_loc < 1) + current_loc = GLOB.plumbing_layers.len + current_layer = GLOB.plumbing_layers[current_loc] + to_chat(source, span_notice("You set the layer to [current_layer].")) /obj/item/construction/plumbing/research name = "research plumbing constructor" @@ -1285,17 +1406,18 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) /obj/item/construction/plumbing/research/set_plumbing_designs() plumbing_design_types = list( - /obj/machinery/plumbing/input = 5, - /obj/machinery/plumbing/output = 5, - /obj/machinery/plumbing/tank = 20, - /obj/machinery/plumbing/acclimator = 10, - /obj/machinery/plumbing/filter = 5, - /obj/machinery/plumbing/grinder_chemical = 30, - /obj/machinery/plumbing/reaction_chamber = 15, - /obj/machinery/plumbing/splitter = 5, - /obj/machinery/plumbing/disposer = 10, - /obj/machinery/plumbing/growing_vat = 20 -) + /obj/machinery/duct = 1, + /obj/machinery/plumbing/input = 5, + /obj/machinery/plumbing/output = 5, + /obj/machinery/plumbing/tank = 20, + /obj/machinery/plumbing/acclimator = 10, + /obj/machinery/plumbing/filter = 5, + /obj/machinery/plumbing/reaction_chamber = 15, + /obj/machinery/plumbing/grinder_chemical = 30, + /obj/machinery/plumbing/splitter = 5, + /obj/machinery/plumbing/disposer = 10, + /obj/machinery/plumbing/growing_vat = 20 + ) /obj/item/rcd_upgrade diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm index cba7f2b01b9..68d86eb44e5 100644 --- a/code/game/objects/structures/lavaland/geyser.dm +++ b/code/game/objects/structures/lavaland/geyser.dm @@ -139,9 +139,6 @@ ///What layer we set it to var/target_layer = DUCT_LAYER_DEFAULT - ///Assoc list for possible layers - var/list/layers = list("Second Layer" = SECOND_DUCT_LAYER, "Default Layer" = DUCT_LAYER_DEFAULT, "Fourth Layer" = FOURTH_DUCT_LAYER) - /obj/item/plunger/attack_atom(obj/O, mob/living/user, params) if(layer_mode) SEND_SIGNAL(O, COMSIG_MOVABLE_CHANGE_DUCT_LAYER, O, target_layer) @@ -178,10 +175,10 @@ if(!istype(user) || !user.canUseTopic(src, BE_CLOSE)) return - var/new_layer = tgui_input_list(user, "Select a layer", "Layer", layers) + var/new_layer = tgui_input_list(user, "Select a layer", "Layer", GLOB.plumbing_layers) if(isnull(new_layer)) return - target_layer = layers[new_layer] + target_layer = GLOB.plumbing_layers[new_layer] ///A faster reinforced plunger /obj/item/plunger/reinforced diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index 295cc7b790d..6faa86ab488 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -7,6 +7,7 @@ All the important duct code: name = "fluid duct" icon = 'icons/obj/plumbing/fluid_ducts.dmi' icon_state = "nduct" + layer = PLUMBING_PIPE_VISIBILE_LAYER use_power = NO_POWER_USE @@ -22,7 +23,7 @@ All the important duct code: var/capacity = 10 ///the color of our duct - var/duct_color = null + var/duct_color = COLOR_VERY_LIGHT_GRAY ///TRUE to ignore colors, so yeah we also connect with other colors without issue var/ignore_colors = FALSE ///1,2,4,8,16 @@ -35,122 +36,113 @@ All the important duct code: var/active = TRUE ///track ducts we're connected to. Mainly for ducts we connect to that we normally wouldn't, like different layers and colors, for when we regenerate the ducts var/list/neighbours = list() - ///wheter we just unanchored or drop whatever is in the variable. either is safe + ///what stack to drop when disconnected. Must be /obj/item/stack/ducts or a subtype var/drop_on_wrench = /obj/item/stack/ducts -/obj/machinery/duct/Initialize(mapload, no_anchor, color_of_duct = "#ffffff", layer_of_duct = DUCT_LAYER_DEFAULT, force_connects) +/obj/machinery/duct/Initialize(mapload, no_anchor, color_of_duct = null, layer_of_duct = null, force_connects, force_ignore_colors) . = ..() + if(force_connects) + connects = force_connects //skip change_connects() because we're still initializing and we need to set our connects at one point + if(!lock_layers && layer_of_duct) + duct_layer = layer_of_duct + if(force_ignore_colors) + ignore_colors = force_ignore_colors + if(!ignore_colors && color_of_duct) + duct_color = color_of_duct + if(duct_color) + add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY) + if(no_anchor) active = FALSE set_anchored(FALSE) else if(!can_anchor()) - qdel(src) - CRASH("Overlapping ducts detected") - - if(force_connects) - connects = force_connects //skip change_connects() because we're still initializing and we need to set our connects at one point - if(!lock_layers) - duct_layer = layer_of_duct - if(!ignore_colors) - duct_color = color_of_duct - if(duct_color) - add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY) + if(mapload) + log_mapping("Overlapping ducts detected at [AREACOORD(src)], unanchoring one.") + // Note that qdeling automatically drops a duct stack + return INITIALIZE_HINT_QDEL handle_layer() - for(var/obj/machinery/duct/D in loc) - if(D == src) - continue - if(D.duct_layer & duct_layer) - return INITIALIZE_HINT_QDEL //If we have company, end it all - attempt_connect() AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) ///start looking around us for stuff to connect to /obj/machinery/duct/proc/attempt_connect() - - for(var/atom/movable/AM in loc) - for(var/datum/component/plumbing/plumber as anything in AM.GetComponents(/datum/component/plumbing)) - if(plumber.active) - disconnect_duct() //let's not built under plumbing machinery - return - - for(var/D in GLOB.cardinals) - if(dumb && !(D & connects)) + for(var/direction in GLOB.cardinals) + if(dumb && !(direction & connects)) continue - for(var/atom/movable/AM in get_step(src, D)) - if(connect_network(AM, D)) - add_connects(D) + for(var/atom/movable/duct_candidate in get_step(src, direction)) + if(connect_network(duct_candidate, direction)) + add_connects(direction) update_appearance() ///see if whatever we found can be connected to -/obj/machinery/duct/proc/connect_network(atom/movable/AM, direction, ignore_color) - if(istype(AM, /obj/machinery/duct)) - return connect_duct(AM, direction, ignore_color) +/obj/machinery/duct/proc/connect_network(atom/movable/plumbable, direction) + if(istype(plumbable, /obj/machinery/duct)) + return connect_duct(plumbable, direction) - for(var/datum/component/plumbing/plumber as anything in AM.GetComponents(/datum/component/plumbing)) + for(var/datum/component/plumbing/plumber as anything in plumbable.GetComponents(/datum/component/plumbing)) . += connect_plumber(plumber, direction) //so that if one is true, all is true. beautiful. ///connect to a duct -/obj/machinery/duct/proc/connect_duct(obj/machinery/duct/D, direction, ignore_color) +/obj/machinery/duct/proc/connect_duct(obj/machinery/duct/other, direction) var/opposite_dir = turn(direction, 180) - if(!active || !D.active) + if(!active || !other.active) return - if(!dumb && D.dumb && !(opposite_dir & D.connects)) + if(!dumb && other.dumb && !(opposite_dir & other.connects)) return - if(dumb && D.dumb && !(connects & D.connects)) //we eliminated a few more scenarios in attempt connect + if(dumb && other.dumb && !(connects & other.connects)) //we eliminated a few more scenarios in attempt connect return - if((duct == D.duct) && duct)//check if we're not just comparing two null values - add_neighbour(D, direction) + if((duct == other.duct) && duct)//check if we're not just comparing two null values + add_neighbour(other, direction) - D.add_connects(opposite_dir) - D.update_appearance() + other.add_connects(opposite_dir) + other.update_appearance() return TRUE //tell the current pipe to also update it's sprite - if(!(D in neighbours)) //we cool - if((duct_color != D.duct_color) && !(ignore_colors || D.ignore_colors)) + if(!(other in neighbours)) //we cool + if((duct_color != other.duct_color) && !(ignore_colors || other.ignore_colors)) return - if(!(duct_layer & D.duct_layer)) + if(!(duct_layer & other.duct_layer)) return - if(D.duct) + if(other.duct) if(duct) - duct.assimilate(D.duct) + duct.assimilate(other.duct) else - D.duct.add_duct(src) + other.duct.add_duct(src) else if(duct) - duct.add_duct(D) + duct.add_duct(other) else create_duct() - duct.add_duct(D) + duct.add_duct(other) - add_neighbour(D, direction) + add_neighbour(other, direction) //Delegate to timer subsystem so its handled the next tick and doesnt cause byond to mistake it for an infinite loop and kill the game - addtimer(CALLBACK(D, .proc/attempt_connect)) + addtimer(CALLBACK(other, .proc/attempt_connect)) return TRUE ///connect to a plumbing object -/obj/machinery/duct/proc/connect_plumber(datum/component/plumbing/P, direction) +/obj/machinery/duct/proc/connect_plumber(datum/component/plumbing/plumbing, direction) var/opposite_dir = turn(direction, 180) - if(duct_layer != P.ducting_layer) + if(!(duct_layer & plumbing.ducting_layer)) return FALSE - if(!P.active) + if(!plumbing.active) return - var/comp_directions = P.supply_connects + P.demand_connects //they should never, ever have supply and demand connects overlap or catastrophic failure + var/comp_directions = plumbing.supply_connects + plumbing.demand_connects //they should never, ever have supply and demand connects overlap or catastrophic failure if(opposite_dir & comp_directions) if(!duct) create_duct() - if(duct.add_plumber(P, opposite_dir)) - neighbours[P.parent] = direction + if(duct.add_plumber(plumbing, opposite_dir)) + neighbours[plumbing.parent] = direction return TRUE ///we disconnect ourself from our neighbours. we also destroy our ductnet and tell our neighbours to make a new one @@ -164,9 +156,12 @@ All the important duct code: reset_connects(0) update_appearance() if(ispath(drop_on_wrench)) - new drop_on_wrench(drop_location()) + var/obj/item/stack/ducts/duct_stack = new drop_on_wrench(drop_location()) + duct_stack.duct_color = GLOB.pipe_color_name[duct_color] || DUCT_COLOR_OMNI + duct_stack.duct_layer = GLOB.plumbing_layer_names["[duct_layer]"] || GLOB.plumbing_layer_names["[DUCT_LAYER_DEFAULT]"] + duct_stack.add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY) drop_on_wrench = null - if(!QDELETED(src)) + if(!QDELING(src)) qdel(src) ///Special proc to draw a new connect frame based on neighbours. not the norm so we can support multiple duct kinds @@ -184,16 +179,17 @@ All the important duct code: duct.add_duct(src) ///add a duct as neighbour. this means we're connected and will connect again if we ever regenerate -/obj/machinery/duct/proc/add_neighbour(obj/machinery/duct/D, direction) - if(!(D in neighbours)) - neighbours[D] = direction - if(!(src in D.neighbours)) - D.neighbours[src] = turn(direction, 180) +/obj/machinery/duct/proc/add_neighbour(obj/machinery/duct/other, direction) + if(!(other in neighbours)) + neighbours[other] = direction + if(!(src in other.neighbours)) + other.neighbours[src] = turn(direction, 180) ///remove all our neighbours, and remove us from our neighbours aswell /obj/machinery/duct/proc/lose_neighbours() - for(var/obj/machinery/duct/D in neighbours) - D.neighbours.Remove(src) + for(var/obj/machinery/duct/other in neighbours) + other.neighbours.Remove(src) + other.generate_connects() neighbours = list() ///add a connect direction @@ -214,24 +210,24 @@ All the important duct code: ///get a list of the ducts we can connect to if we are dumb /obj/machinery/duct/proc/get_adjacent_ducts() var/list/adjacents = list() - for(var/A in GLOB.cardinals) - if(A & connects) - for(var/obj/machinery/duct/D in get_step(src, A)) - if((turn(A, 180) & D.connects) && D.active) - adjacents += D + for(var/direction in GLOB.cardinals) + if(direction & connects) + for(var/obj/machinery/duct/other in get_step(src, direction)) + if((turn(direction, 180) & other.connects) && other.active) + adjacents += other return adjacents /obj/machinery/duct/update_icon_state() var/temp_icon = initial(icon_state) - for(var/D in GLOB.cardinals) - if(D & connects) - if(D == NORTH) + for(var/direction in GLOB.cardinals) + switch(direction & connects) + if(NORTH) temp_icon += "_n" - if(D == SOUTH) + if(SOUTH) temp_icon += "_s" - if(D == EAST) + if(EAST) temp_icon += "_e" - if(D == WEST) + if(WEST) temp_icon += "_w" icon_state = temp_icon return ..() @@ -239,7 +235,8 @@ All the important duct code: ///update the layer we are on /obj/machinery/duct/proc/handle_layer() var/offset - switch(duct_layer)//it's a bitfield, but it's fine because it only works when there's one layer, and multiple layers should be handled differently + //it's a bitfield, but it's fine because ducts themselves are only on one layer + switch(duct_layer) if(FIRST_DUCT_LAYER) offset = -10 if(SECOND_DUCT_LAYER) @@ -253,6 +250,7 @@ All the important duct code: pixel_x = offset pixel_y = offset + layer = initial(layer) + duct_layer * 0.0003 /obj/machinery/duct/set_anchored(anchorvalue) . = ..() @@ -264,10 +262,10 @@ All the important duct code: else disconnect_duct(TRUE) -/obj/machinery/duct/wrench_act(mob/living/user, obj/item/I) //I can also be the RPD +/obj/machinery/duct/wrench_act(mob/living/user, obj/item/wrench) //I can also be the RPD ..() add_fingerprint(user) - I.play_tool_sound(src) + wrench.play_tool_sound(src) if(anchored || can_anchor()) set_anchored(!anchored) user.visible_message( \ @@ -277,14 +275,15 @@ All the important duct code: return TRUE ///collection of all the sanity checks to prevent us from stacking ducts that shouldn't be stacked -/obj/machinery/duct/proc/can_anchor(turf/T) - if(!T) - T = get_turf(src) - for(var/obj/machinery/duct/D in T) - if(!anchored || D == src) - continue - for(var/A in GLOB.cardinals) - if(A & connects && A & D.connects) +/obj/machinery/duct/proc/can_anchor(turf/destination) + if(!destination) + destination = get_turf(src) + for(var/obj/machinery/duct/other in destination) + if(other.anchored && other != src && (duct_layer & other.duct_layer)) + return FALSE + for(var/obj/machinery/machine in destination) + for(var/datum/component/plumbing/plumber as anything in machine.GetComponents(/datum/component/plumbing)) + if(plumber.ducting_layer & duct_layer) return FALSE return TRUE @@ -297,26 +296,29 @@ All the important duct code: disconnect_duct() return ..() -/obj/machinery/duct/MouseDrop_T(atom/A, mob/living/user) - if(!istype(A, /obj/machinery/duct)) +/obj/machinery/duct/MouseDrop_T(atom/drag_source, mob/living/user) + if(!istype(drag_source, /obj/machinery/duct)) return - var/obj/machinery/duct/D = A - var/obj/item/I = user.get_active_held_item() - if(I?.tool_behaviour != TOOL_WRENCH) - to_chat(user, span_warning("You need to be holding a wrench in your active hand to do that!")) + var/obj/machinery/duct/other = drag_source + if(get_dist(src, other) != 1) return - if(get_dist(src, D) != 1) - return - var/direction = get_dir(src, D) + var/direction = get_dir(src, other) if(!(direction in GLOB.cardinals)) return - if(duct_layer != D.duct_layer) + if(!(duct_layer & other.duct_layer)) + to_chat(user, span_warning("The ducts must be on the same layer to connect them!")) + return + var/obj/item/held_item = user.get_active_held_item() + if(held_item?.tool_behaviour != TOOL_WRENCH) + to_chat(user, span_warning("You need to be holding a wrench in your active hand to do that!")) return add_connects(direction) //the connect of the other duct is handled in connect_network, but do this here for the parent duct because it's not necessary in normal cases - add_neighbour(D, direction) - connect_network(D, direction, TRUE) + add_neighbour(other, direction) + connect_network(other, direction) update_appearance() + held_item.play_tool_sound(src) + to_chat(user, span_notice("You connect the two plumbing ducts.")) /obj/item/stack/ducts name = "stack of duct" @@ -330,22 +332,21 @@ All the important duct code: max_amount = 50 item_flags = NOBLUDGEON merge_type = /obj/item/stack/ducts + matter_amount = 1 ///Color of our duct var/duct_color = "omni" ///Default layer of our duct var/duct_layer = "Default Layer" - ///Assoc index with all the available layers. yes five might be a bit much. Colors uses a global by the way - var/list/layers = list("Second Layer" = SECOND_DUCT_LAYER, "Default Layer" = DUCT_LAYER_DEFAULT, "Fourth Layer" = FOURTH_DUCT_LAYER) /obj/item/stack/ducts/examine(mob/user) . = ..() . += span_notice("It's current color and layer are [duct_color] and [duct_layer]. Use in-hand to change.") /obj/item/stack/ducts/attack_self(mob/user) - var/new_layer = tgui_input_list(user, "Select a layer", "Layer", layers) + var/new_layer = tgui_input_list(user, "Select a layer", "Layer", GLOB.plumbing_layers, duct_layer) if(new_layer) duct_layer = new_layer - var/new_color = tgui_input_list(user, "Select a color", "Color", GLOB.pipe_paint_colors) + var/new_color = tgui_input_list(user, "Select a color", "Color", GLOB.pipe_paint_colors, duct_color) if(new_color) duct_color = new_color add_atom_colour(GLOB.pipe_paint_colors[new_color], FIXED_COLOUR_PRIORITY) @@ -355,16 +356,25 @@ All the important duct code: if(!proximity) return if(istype(target, /obj/machinery/duct)) - var/obj/machinery/duct/D = target - if(!D.anchored) - add(1) - qdel(D) + var/obj/machinery/duct/duct = target + if(duct.anchored) + to_chat(user, span_warning("The duct must be unanchored before it can be picked up.")) + return + + // Turn into a duct stack and then merge to the in-hand stack. + var/obj/item/stack/ducts/stack = new(duct.loc, 1, FALSE) + qdel(duct) + if(stack.can_merge(src)) + stack.merge(src) + return + check_attach_turf(target) /obj/item/stack/ducts/proc/check_attach_turf(atom/target) - if(istype(target, /turf/open) && use(1)) + if(isopenturf(target) && use(1)) var/turf/open/open_turf = target - new /obj/machinery/duct(open_turf, FALSE, GLOB.pipe_paint_colors[duct_color], layers[duct_layer]) + var/is_omni = duct_color == DUCT_COLOR_OMNI + new /obj/machinery/duct(open_turf, FALSE, GLOB.pipe_paint_colors[duct_color], GLOB.plumbing_layers[duct_layer], null, is_omni) playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) /obj/item/stack/ducts/fifty diff --git a/code/modules/plumbing/plumbers/_plumb_machinery.dm b/code/modules/plumbing/plumbers/_plumb_machinery.dm index f9930bf5f6b..54cea5f5ca2 100644 --- a/code/modules/plumbing/plumbers/_plumb_machinery.dm +++ b/code/modules/plumbing/plumbers/_plumb_machinery.dm @@ -14,7 +14,6 @@ var/buffer = 50 ///Flags for reagents, like INJECTABLE, TRANSPARENT bla bla everything thats in DEFINES/reagents.dm var/reagent_flags = TRANSPARENT - ///wheter we partake in rcd construction or not /obj/machinery/plumbing/Initialize(mapload, bolt = TRUE) . = ..() @@ -98,6 +97,8 @@ /obj/machinery/plumbing/layer_manifold/Initialize(mapload, bolt, layer) . = ..() + AddComponent(/datum/component/plumbing/manifold, bolt, FIRST_DUCT_LAYER) AddComponent(/datum/component/plumbing/manifold, bolt, SECOND_DUCT_LAYER) AddComponent(/datum/component/plumbing/manifold, bolt, THIRD_DUCT_LAYER) AddComponent(/datum/component/plumbing/manifold, bolt, FOURTH_DUCT_LAYER) + AddComponent(/datum/component/plumbing/manifold, bolt, FIFTH_DUCT_LAYER) diff --git a/icons/hud/radial.dmi b/icons/hud/radial.dmi index 5e3016009564d9e04d66b88dbd687f637a530c75..54b28e2f36fb6b09ca2f0357ea94e3e65a9eda34 100644 GIT binary patch literal 27711 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%0D!c! zwRQpk2zvbj60mh-3{n-h{^;UPIAa?c8Za2l2>=jdFs171>oB0~=nyn|yYOS=EW1HTk1Li671w~8UQEX`mPVX5zc3Wt$l*L0tr`x2}FMY2+K(vu#kx{ z*xt{H@9=mDPDk8D1%e!6-w-6-SqQvH z0k`i*y$3z_SU=yIswUkQ(%nHc7FTL{Qcpsd4Cx*}S5xIzvsHKlycait*_AX}4_wrf z)K!i(-d3GdA{BdpUAui^m}caH4VjP{mx1Vk&e5B?M*u(~%g)-uIV@)`_khDNs2CqA z^W~ZCrWlPJbUOk(1m3N+47@RnZdZaT?Vi?hER(1FWoMBb7;pPly_|M*ZAwtvQt_u) zbv(CP{OjVKH^0~Wc3k8#d|V!FOR5mMWXRph=CozWB+yW#mq?ZfuIN8HXOn*wMy!7i zx%?^RctH7;WNKU#d+;-dK{K*7X_<-C4X8}lXJ=10lt0p6a?8u%Q>a3EX8^zSHG$(P z_IhOhI$0RP_lepX?{ZpjkGN3AedH~wx!`(X(j=f==2XBhtv-?WIACe)OS_w8`}jp& zP^EUESK}~*#F`z{poB_EES9;FLECqa8xAulea+e<0S;kOeMDmI8YAdt7y0$i#THBD zlyXj@^n&9>fKE6d_RsJdMaBAN)o?(YrzmL<4CC?~=hW z$&n}tX_JNJA8<9K>I-V}HS{?Pm}By);ao1~%kjb}3*k(}LOl0_^9G~}9#i4V)E(Tn z$)4Y;-(LqA$Xu3rSDgVZju@vBqMNMFn3f(dfABo*obEh(SO>am3;Y&nD`BZ2pBh@@ znGw3GpodVaYe3k`5|$JUv<^)-D!2X5=~IOUwv!odL1tF;g%`|l0<3knehfRLC#n*O zrn2CAcuQgDa6$(Y`xq14MGgcf(wMk2=oMM`xce!z=7t(!h4fUAT;-u^j_0BLYR&SV zNII>BQw~-Hb;mO9O$rj_Y`&SYL2qNGYseU_^y<8B+M`f1l)PM-*^%&y9h+Sl5ZSZ1 zdLLDpRDA!&3c_Ezn{IM$}y|Q!=-#V^+%Id7C zx3Vjl(CNNp4D8Dps3h4Ji?*Zx3j(^!BA+UY!ikQzU}7W8X)4kV52#9jtTTTKt|Ma$X=knN$(L&X=U;`9k@V}sU&i56!Eks7tr^G6S( z3Qe}Dcb`7;r!flBJ9oedf%k{c=*}lARsPe1fAetTur$4sE)~64<`uqJDfFAN2~-#{ zfBxji>?aF>yXsm1jYseHlbqC<6&Gbq&2DsDUH z+=@sxNj!L&`azAe!iipaeD?tj{+s4?35E*2#k|L{@ee2hKxobG)u`zRP%U%AJb+L<#tHX@snHWuBF$p@?+B?Hj`oH84 zkWPDeXI83WRurm9Ywc|UJ)%f{&Bz#>tJ1G=^67k D@ZrwE diff --git a/icons/obj/plumbing/fluid_ducts.dmi b/icons/obj/plumbing/fluid_ducts.dmi index d911e25b9c9262046862332819a328c4a2596977..f28a4c35853e066c81d6b72e34f64578e693681b 100644 GIT binary patch delta 890 zcmV-=1BLv}2ZRTZ7k_{R0{{R3eocQU0000gP)t-s0000U9v)3iO`DsW!otFAY;5lC z?pfFDZ*Bkpc$`yKaB_9`^iy#0_2eo`Eh^5;&r`5fFwryM;(sj9Oex6#a*U0*I5Sc+ z(=$pSoZ^zil2jm5DKDioxkQPJGbOXA7|1u|;!G<_%uR)`QB}p~5vi`2NOh@1sw*ef zeZ<>Lf|xq%4PAjO1Z6|5@Wh)8wDxYQ{txca$( zvk?HfVD?*Tw||f500072Nkl)KnlLu_>@YVv`mYMMxF2 zoFL@}9D$yI8UhDQ?w#t74FVxHMHrt@xj z4BR4my>5Zg`6%fWr~w$bV_-ypWOHCteUIrLQmYO6vwzuaIu~CuI}rEjx@H{@FV!Hc zPdcDg)A{VcL9e}@aQ6pmbRt*OANou)v(DhbKspVBQr~^$G z1p|R}Q9`Q0TGv(e-bM^;4b=8sTcEb@S_56d!2JV8A18i#=kh^+U)=k*9s?Q3KtCYd z17etu9e?)-TCafQdn7}09()jgFsxTV@;&lDnqPnqzBYkRi{^R-B;Os|=gACYVCTR?!>AST z`;+4Rs~e#DsLcS>{I33KRXO^Rc{y52HCh^gn%@P{jZ~lF8Shcnqqtw^2Pz*MfSTV0 z(Q~bAsRXLL1gd;&I3ehJ0(c=?DxUGGJiCVc$^b;)69t#DGTZpQvm~wxhZ1FKM<=(&uMwoj8w+jX`a1Vgq&yTI~3xQnHIMj2) QUH||907*qoM6N<$f~Dn}lmGw# delta 964 zcmV;#13UbL2h9hN7k{7x0{{R3dEt5<0000IP)t-s0000pGc#XbU$U~YmX?;Iqod?; z13>@)00DGTPE!Ct=GbNc006*xR9JLGWpiV4X>fFDZ*Bkpc$}4!u@1r@7=&kd3exUI z+paFAjp<-tfix`&NreP{ZQ|35?bJa%w*Qik<9>4F;?$hmPJh&wmOqsb##wO`)o#z( zY(~;ilm%y6nVY6n1nNn}F|*@ipp3K8_z?Ahv#am#YMr-~v>+EiPJ!Hv=EHAJ@I+Gv z*fYSMHf8rD7T+rAtrp`1;~}(xLh<{ zy?o|3F^wMmJ%4ox5C8xIok>JNRA_|s7ZHf_v~APMg#xWSUv#)AZhy3!Q!+2@>X^>c)!`=Ry26eE z+YK$$RRMSavp(PCTvs?k_cy1s+uWttJ7nt{^xUKo*qfA60}jYafun)!IxOxXw+_#5 z(Nf^#=1FYtxCU~+$PGz6NCp$^7}`iH$UKku4amwBe?c) ze*)Ybh=2A080?#`ff0JYm;<+GqLaf0-VY+sA8_c9L)_KpCcif1N>W1H)z_XH@}vwD z`xv4?1wJK^vm#=k)C^_M&k#%P8t$L9=XQ|$M{WpW|A;49`j#ZCSD*qF*b78!!C4Ko zDhsGLXnr^|gl4LXqgg_G#`!;YNaXF^XRvb!;D7ud0`LF^E$+Y}S5HCq&TwE-s!1{x zannZpn4v%gDsYI#nkUp)dq;L~j4De&h)YAT&^U>Q0x=2SKWY5+(G?UpLm)Td6*xyA zSP!4}`+>n6r7y$kz;hz!eiKE12=D*uKmg=+t}DU&zd8^NXVycsw9+2JDKMGo=78z; z>VLGsRZM{|4IH`zC~!P*YO^$=u1D2930>V5l&T9z}ue9QoH?Nz+ mKLh8#ro0AlTC+5@o%;ivs8`*T!3U`T0000