Files
Bubberstation/code/game/objects/items/RPD.dm
SkyratBot b116ec2789 [MIRROR] TGUI replaces Radial menu for Plumbing RCD [MDB IGNORE] (#17840)
* TGUI replaces Radial menu for Plumbing RCD (#71569)

**About the pull request**
Radial menu's replaced with TGUI window for all plumbing RCD's

https://user-images.githubusercontent.com/110812394/204481973-8f6d62c0-9288-4165-bce7-ee8bfec95bee.mp4

**Why it's good for the game**

**1. Faster Navigation**
- Old System: It had 3 radial menus'
   ->To select type of device to build
   ->To select the colour of fluid ducts
   ->To select the layer of devices
3 menus for one system makes navigation between them annoying for e.g.:
to create a red fluid duct of layer 5 you have to open all these 3 menus
& cycling through the radial menu by clicking the next button is time
consuming when you have a large number of items
- New System: with just one window open it's as simple as 3 clicks and
your done just like the RPD

**2. Always available**
- Old System: When you select an option inside a radial menu the menu
closes so you have to reopen it again to change a setting
- New System: open the window drag it to the side and it will always be
there for you

**3. Just easier for our eyes**
- Old System: Moving my eyes in a circle across the menu only to find
out i have to click Next and circle my eyes again honestly makes my head
spin
- New System: The menu options are small enough to be viewed all at once
& helps me see what I need more easily

**4. Consistency**
Both the RPD & plumbing all have similar concepts i.e., pipes, devices,
layers & colours it only makes sense they should have similar
interfaces. Also categorizing the items into tabs can help with
explaining what each item does

**Conclusion**
Just because the radial menu looks attractive shouldn't mean it must be
used everywhere, it only makes sense to use it when the number of
options of an item is small enough to be displayed on the screen all at
once for e.g., modules in a Mod suit. If the user has to click the next
button to cycle through the item's, it defeats the purpose of the radial
menu and makes the user work more to find his settings

**Side Note :** removed unused code in RapidPipeDispenser.js

**ChangeLog**

🆑
add: plumbing constructor TGUI, just like Rapid Pipe Dispensers
del: plumbing constructor radials
code: categories for each item
/🆑

Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com>
Co-authored-by: Jeremiah <42397676+jlsnow301@ users.noreply.github.com>
Co-authored-by: Time-Green <timkoster1@ hotmail.com>

* TGUI replaces Radial menu for Plumbing RCD

Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com>
Co-authored-by: Jeremiah <42397676+jlsnow301@ users.noreply.github.com>
Co-authored-by: Time-Green <timkoster1@ hotmail.com>
2022-12-01 10:31:31 -05:00

673 lines
27 KiB
Plaintext

/*
CONTAINS:
RPD
*/
#define ATMOS_CATEGORY 0
#define DISPOSALS_CATEGORY 1
#define TRANSIT_CATEGORY 2
#define BUILD_MODE (1<<0)
#define WRENCH_MODE (1<<1)
#define DESTROY_MODE (1<<2)
#define REPROGRAM_MODE (1<<3)
GLOBAL_LIST_INIT(atmos_pipe_recipes, list(
"Pipes" = list(
new /datum/pipe_info/pipe("Pipe", /obj/machinery/atmospherics/pipe/smart, TRUE),
new /datum/pipe_info/pipe("Layer Adapter", /obj/machinery/atmospherics/pipe/layer_manifold, TRUE),
new /datum/pipe_info/pipe("Color Adapter", /obj/machinery/atmospherics/pipe/color_adapter, TRUE),
new /datum/pipe_info/pipe("Bridge Pipe", /obj/machinery/atmospherics/pipe/bridge_pipe, TRUE),
new /datum/pipe_info/pipe("Multi-Deck Adapter", /obj/machinery/atmospherics/pipe/multiz, FALSE),
),
"Devices" = list(
new /datum/pipe_info/pipe("Connector", /obj/machinery/atmospherics/components/unary/portables_connector, TRUE),
new /datum/pipe_info/pipe("Gas Pump", /obj/machinery/atmospherics/components/binary/pump, TRUE),
new /datum/pipe_info/pipe("Volume Pump", /obj/machinery/atmospherics/components/binary/volume_pump, TRUE),
new /datum/pipe_info/pipe("Gas Filter", /obj/machinery/atmospherics/components/trinary/filter, TRUE),
new /datum/pipe_info/pipe("Gas Mixer", /obj/machinery/atmospherics/components/trinary/mixer, TRUE),
new /datum/pipe_info/pipe("Passive Gate", /obj/machinery/atmospherics/components/binary/passive_gate, TRUE),
new /datum/pipe_info/pipe("Injector", /obj/machinery/atmospherics/components/unary/outlet_injector, TRUE),
new /datum/pipe_info/pipe("Scrubber", /obj/machinery/atmospherics/components/unary/vent_scrubber, TRUE),
new /datum/pipe_info/pipe("Unary Vent", /obj/machinery/atmospherics/components/unary/vent_pump, TRUE),
new /datum/pipe_info/pipe("Passive Vent", /obj/machinery/atmospherics/components/unary/passive_vent, TRUE),
new /datum/pipe_info/pipe("Manual Valve", /obj/machinery/atmospherics/components/binary/valve, TRUE),
new /datum/pipe_info/pipe("Digital Valve", /obj/machinery/atmospherics/components/binary/valve/digital, TRUE),
new /datum/pipe_info/pipe("Pressure Valve", /obj/machinery/atmospherics/components/binary/pressure_valve, TRUE),
new /datum/pipe_info/pipe("Temperature Gate", /obj/machinery/atmospherics/components/binary/temperature_gate, TRUE),
new /datum/pipe_info/pipe("Temperature Pump", /obj/machinery/atmospherics/components/binary/temperature_pump, TRUE),
new /datum/pipe_info/meter("Meter"),
),
"Heat Exchange" = list(
new /datum/pipe_info/pipe("Pipe", /obj/machinery/atmospherics/pipe/heat_exchanging/simple, FALSE),
new /datum/pipe_info/pipe("Manifold", /obj/machinery/atmospherics/pipe/heat_exchanging/manifold, FALSE),
new /datum/pipe_info/pipe("4-Way Manifold", /obj/machinery/atmospherics/pipe/heat_exchanging/manifold4w, FALSE),
new /datum/pipe_info/pipe("Junction", /obj/machinery/atmospherics/pipe/heat_exchanging/junction, FALSE),
new /datum/pipe_info/pipe("Heat Exchanger", /obj/machinery/atmospherics/components/unary/heat_exchanger, FALSE),
)
))
GLOBAL_LIST_INIT(disposal_pipe_recipes, list(
"Disposal Pipes" = list(
new /datum/pipe_info/disposal("Pipe", /obj/structure/disposalpipe/segment, PIPE_BENDABLE),
new /datum/pipe_info/disposal("Junction", /obj/structure/disposalpipe/junction, PIPE_TRIN_M),
new /datum/pipe_info/disposal("Y-Junction", /obj/structure/disposalpipe/junction/yjunction),
new /datum/pipe_info/disposal("Sort Junction", /obj/structure/disposalpipe/sorting/mail, PIPE_TRIN_M),
new /datum/pipe_info/disposal("Trunk", /obj/structure/disposalpipe/trunk),
new /datum/pipe_info/disposal("Bin", /obj/machinery/disposal/bin, PIPE_ONEDIR),
new /datum/pipe_info/disposal("Outlet", /obj/structure/disposaloutlet),
new /datum/pipe_info/disposal("Chute", /obj/machinery/disposal/delivery_chute),
)
))
GLOBAL_LIST_INIT(transit_tube_recipes, list(
"Transit Tubes" = list(
new /datum/pipe_info/transit("Straight Tube", /obj/structure/c_transit_tube, PIPE_STRAIGHT),
new /datum/pipe_info/transit("Straight Tube with Crossing", /obj/structure/c_transit_tube/crossing, PIPE_STRAIGHT),
new /datum/pipe_info/transit("Curved Tube", /obj/structure/c_transit_tube/curved, PIPE_UNARY_FLIPPABLE),
new /datum/pipe_info/transit("Diagonal Tube", /obj/structure/c_transit_tube/diagonal, PIPE_STRAIGHT),
new /datum/pipe_info/transit("Diagonal Tube with Crossing", /obj/structure/c_transit_tube/diagonal/crossing, PIPE_STRAIGHT),
new /datum/pipe_info/transit("Junction", /obj/structure/c_transit_tube/junction, PIPE_UNARY_FLIPPABLE),
),
"Station Equipment" = list(
new /datum/pipe_info/transit("Through Tube Station", /obj/structure/c_transit_tube/station, PIPE_STRAIGHT),
new /datum/pipe_info/transit("Terminus Tube Station", /obj/structure/c_transit_tube/station/reverse, PIPE_UNARY),
new /datum/pipe_info/transit("Through Tube Dispenser Station", /obj/structure/c_transit_tube/station/dispenser, PIPE_STRAIGHT),
new /datum/pipe_info/transit("Terminus Tube Dispenser Station", /obj/structure/c_transit_tube/station/dispenser/reverse, PIPE_UNARY),
new /datum/pipe_info/transit("Transit Tube Pod", /obj/structure/c_transit_tube_pod, PIPE_ONEDIR),
)
))
/datum/pipe_info
var/name
var/icon_state
var/id = -1
var/dirtype = PIPE_BENDABLE
var/all_layers
/datum/pipe_info/proc/Params()
return ""
/datum/pipe_info/proc/get_preview(selected_dir)
var/list/dirs
switch(dirtype)
if(PIPE_STRAIGHT, PIPE_BENDABLE)
dirs = list("[NORTH]" = "Vertical", "[EAST]" = "Horizontal")
if(dirtype == PIPE_BENDABLE)
dirs += list("[NORTHWEST]" = "West to North", "[NORTHEAST]" = "North to East",
"[SOUTHWEST]" = "South to West", "[SOUTHEAST]" = "East to South")
if(PIPE_TRINARY)
dirs = list("[NORTH]" = "West South East", "[SOUTH]" = "East North West",
"[EAST]" = "North West South", "[WEST]" = "South East North")
if(PIPE_TRIN_M)
dirs = list("[NORTH]" = "North East South", "[SOUTHWEST]" = "North West South",
"[NORTHEAST]" = "South East North", "[SOUTH]" = "South West North",
"[WEST]" = "West North East", "[SOUTHEAST]" = "West South East",
"[NORTHWEST]" = "East North West", "[EAST]" = "East South West",)
if(PIPE_UNARY)
dirs = list("[NORTH]" = "North", "[SOUTH]" = "South", "[WEST]" = "West", "[EAST]" = "East")
if(PIPE_ONEDIR)
dirs = list("[SOUTH]" = name)
if(PIPE_UNARY_FLIPPABLE)
dirs = list("[NORTH]" = "North", "[EAST]" = "East", "[SOUTH]" = "South", "[WEST]" = "West",
"[NORTHEAST]" = "North Flipped", "[SOUTHEAST]" = "East Flipped", "[SOUTHWEST]" = "South Flipped", "[NORTHWEST]" = "West Flipped")
var/list/rows = list()
var/list/row = list("previews" = list())
var/i = 0
for(var/dir in dirs)
var/numdir = text2num(dir)
var/flipped = ((dirtype == PIPE_TRIN_M) || (dirtype == PIPE_UNARY_FLIPPABLE)) && (ISDIAGONALDIR(numdir))
row["previews"] += list(list("selected" = (numdir == selected_dir), "dir" = dir2text(numdir), "dir_name" = dirs[dir], "icon_state" = icon_state, "flipped" = flipped))
if(i++ || dirtype == PIPE_ONEDIR)
rows += list(row)
row = list("previews" = list())
i = 0
return rows
/datum/pipe_info/pipe/New(label, obj/machinery/atmospherics/path, use_five_layers)
name = label
id = path
all_layers = use_five_layers
icon_state = initial(path.pipe_state)
var/obj/item/pipe/c = initial(path.construction_type)
dirtype = initial(c.RPD_type)
/datum/pipe_info/pipe/Params()
return "makepipe=[id]&type=[dirtype]"
/datum/pipe_info/meter
icon_state = "meter"
dirtype = PIPE_ONEDIR
/datum/pipe_info/meter/New(label)
name = label
/datum/pipe_info/meter/Params()
return "makemeter=[id]&type=[dirtype]"
/datum/pipe_info/disposal/New(label, obj/path, dt=PIPE_UNARY)
name = label
id = path
icon_state = initial(path.icon_state)
if(ispath(path, /obj/structure/disposalpipe))
icon_state = "con[icon_state]"
dirtype = dt
/datum/pipe_info/disposal/Params()
return "dmake=[id]&type=[dirtype]"
/datum/pipe_info/transit/New(label, obj/path, dt=PIPE_UNARY)
name = label
id = path
dirtype = dt
icon_state = initial(path.icon_state)
if(dt == PIPE_UNARY_FLIPPABLE)
icon_state = "[icon_state]_preview"
/obj/item/pipe_dispenser
name = "Rapid Pipe Dispenser (RPD)"
desc = "A device used to rapidly pipe things."
icon = 'icons/obj/tools.dmi'
icon_state = "rpd"
worn_icon_state = "RPD"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
flags_1 = CONDUCT_1
force = 10
throwforce = 10
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_NORMAL
slot_flags = ITEM_SLOT_BELT
custom_materials = list(/datum/material/iron=75000, /datum/material/glass=37500)
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 100, ACID = 50)
resistance_flags = FIRE_PROOF
///Sparks system used when changing device in the UI
var/datum/effect_system/spark_spread/spark_system
///Direction of the device we are going to spawn, set up in the UI
var/p_dir = NORTH
///Initial direction of the smart pipe we are going to spawn, set up in the UI
var/p_init_dir = ALL_CARDINALS
///Is the device of the flipped type?
var/p_flipped = FALSE
///Color of the device we are going to spawn
var/paint_color = "green"
///Speed of building atmos devices
var/atmos_build_speed = 0.5 SECONDS
///Speed of building disposal devices
var/disposal_build_speed = 0.5 SECONDS
///Speed of building transit devices
var/transit_build_speed = 0.5 SECONDS
///Speed of removal of unwrenched devices
var/destroy_speed = 0.5 SECONDS
///Speed of reprogramming connectable directions of smart pipes
var/reprogram_speed = 0.5 SECONDS
///Category currently active (Atmos, disposal, transit)
var/category = ATMOS_CATEGORY
///Piping layer we are going to spawn the atmos device in
var/piping_layer = PIPING_LAYER_DEFAULT
///Layer for disposal ducts
var/ducting_layer = DUCT_LAYER_DEFAULT
///Stores the current device to spawn
var/datum/pipe_info/recipe
///Stores the first atmos device
var/static/datum/pipe_info/first_atmos
///Stores the first disposal device
var/static/datum/pipe_info/first_disposal
///Stores the first transit device
var/static/datum/pipe_info/first_transit
///The modes that are allowed for the RPD
var/mode = BUILD_MODE | DESTROY_MODE | WRENCH_MODE | REPROGRAM_MODE
/// Bitflags for upgrades
var/upgrade_flags
/obj/item/pipe_dispenser/Initialize(mapload)
. = ..()
spark_system = new
spark_system.set_up(5, 0, src)
spark_system.attach(src)
if(!first_atmos)
first_atmos = GLOB.atmos_pipe_recipes[GLOB.atmos_pipe_recipes[1]][1]
if(!first_disposal)
first_disposal = GLOB.disposal_pipe_recipes[GLOB.disposal_pipe_recipes[1]][1]
if(!first_transit)
first_transit = GLOB.transit_tube_recipes[GLOB.transit_tube_recipes[1]][1]
recipe = first_atmos
/obj/item/pipe_dispenser/Destroy()
qdel(spark_system)
spark_system = null
return ..()
/obj/item/pipe_dispenser/examine(mob/user)
. = ..()
. += "You can scroll your mouse wheel to change the piping layer."
. += "You can right click a pipe to set the RPD to its color and layer."
/obj/item/pipe_dispenser/equipped(mob/user, slot, initial)
. = ..()
if(slot & ITEM_SLOT_HANDS)
RegisterSignal(user, COMSIG_MOUSE_SCROLL_ON, PROC_REF(mouse_wheeled))
else
UnregisterSignal(user,COMSIG_MOUSE_SCROLL_ON)
/obj/item/pipe_dispenser/dropped(mob/user, silent)
UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
return ..()
/obj/item/pipe_dispenser/cyborg_unequip(mob/user)
UnregisterSignal(user, COMSIG_MOUSE_SCROLL_ON)
return ..()
/obj/item/pipe_dispenser/attack_self(mob/user)
ui_interact(user)
/obj/item/pipe_dispenser/pre_attack(atom/target, mob/user, params)
if(istype(target, /obj/item/rpd_upgrade/unwrench))
install_upgrade(target, user)
return TRUE
return ..()
/obj/item/pipe_dispenser/pre_attack_secondary(obj/machinery/atmospherics/target, mob/user, params)
if(!istype(target, /obj/machinery/atmospherics))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(target.pipe_color && target.piping_layer)
paint_color = GLOB.pipe_color_name[target.pipe_color]
piping_layer = target.piping_layer
to_chat(user, span_notice("You change [src] to [paint_color] color and layer [piping_layer] pipes."))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/pipe_dispenser/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/rpd_upgrade))
install_upgrade(W, user)
return TRUE
return ..()
/**
* Installs an upgrade into the RPD
*
* Installs an upgrade into the RPD checking if it is already installed
* Arguments:
* * rpd_up - RPD upgrade
* * user - mob that use upgrade on RPD
*/
/obj/item/pipe_dispenser/proc/install_upgrade(obj/item/rpd_upgrade/rpd_up, mob/user)
if(rpd_up.upgrade_flags& upgrade_flags)
to_chat(user, span_warning("[src] has already installed this upgrade!"))
return
upgrade_flags |= rpd_up.upgrade_flags
playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
qdel(rpd_up)
/obj/item/pipe_dispenser/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] points the end of the RPD down [user.p_their()] throat and presses a button! It looks like [user.p_theyre()] trying to commit suicide..."))
playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE)
playsound(get_turf(user), 'sound/items/deconstruct.ogg', 50, TRUE)
return BRUTELOSS
/obj/item/pipe_dispenser/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/pipes),
)
/obj/item/pipe_dispenser/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "RapidPipeDispenser", name)
ui.open()
/obj/item/pipe_dispenser/ui_static_data(mob/user)
var/list/data = list("paint_colors" = GLOB.pipe_paint_colors)
return data
/obj/item/pipe_dispenser/ui_data(mob/user)
var/list/data = list(
"category" = category,
"piping_layer" = piping_layer,
"ducting_layer" = ducting_layer,
"preview_rows" = recipe.get_preview(p_dir),
"categories" = list(),
"selected_color" = paint_color,
"mode" = mode
)
var/list/recipes
switch(category)
if(ATMOS_CATEGORY)
recipes = GLOB.atmos_pipe_recipes
if(DISPOSALS_CATEGORY)
recipes = GLOB.disposal_pipe_recipes
if(TRANSIT_CATEGORY)
recipes = GLOB.transit_tube_recipes
for(var/c in recipes)
var/list/cat = recipes[c]
var/list/r = list()
for(var/i in 1 to cat.len)
var/datum/pipe_info/info = cat[i]
r += list(list("pipe_name" = info.name, "pipe_index" = i, "selected" = (info == recipe), "all_layers" = info.all_layers))
if(info == recipe)
data["selected_category"] = c
data["categories"] += list(list("cat_name" = c, "recipes" = r))
var/list/init_directions = list("north" = FALSE, "south" = FALSE, "east" = FALSE, "west" = FALSE)
for(var/direction in GLOB.cardinals)
if(p_init_dir & direction)
init_directions[dir2text(direction)] = TRUE
data["init_directions"] = init_directions
return data
/obj/item/pipe_dispenser/ui_act(action, params)
. = ..()
if(.)
return
if(!usr.canUseTopic(src, be_close = TRUE))
return
var/playeffect = TRUE
switch(action)
if("color")
paint_color = params["paint_color"]
if("category")
category = text2num(params["category"])
switch(category)
if(DISPOSALS_CATEGORY)
recipe = first_disposal
if(ATMOS_CATEGORY)
recipe = first_atmos
if(TRANSIT_CATEGORY)
recipe = first_transit
p_dir = NORTH
playeffect = FALSE
if("piping_layer")
piping_layer = text2num(params["piping_layer"])
playeffect = FALSE
if("ducting_layer")
ducting_layer = text2num(params["ducting_layer"])
playeffect = FALSE
if("pipe_type")
var/static/list/recipes
if(!recipes)
recipes = GLOB.disposal_pipe_recipes + GLOB.atmos_pipe_recipes + GLOB.transit_tube_recipes
recipe = recipes[params["category"]][text2num(params["pipe_type"])]
p_dir = NORTH
if("setdir")
p_dir = text2dir(params["dir"])
p_flipped = text2num(params["flipped"])
playeffect = FALSE
if("mode")
var/n = text2num(params["mode"])
mode ^= n
if("init_dir_setting")
var/target_dir = p_init_dir ^ text2dir(params["dir_flag"])
// Refuse to create a smart pipe that can only connect in one direction (it would act weirdly and lack an icon)
if (ISNOTSTUB(target_dir))
p_init_dir = target_dir
else
to_chat(usr, span_warning("\The [src]'s screen flashes a warning: Can't configure a pipe to only connect in one direction."))
playeffect = FALSE
if("init_reset")
p_init_dir = ALL_CARDINALS
if(playeffect)
spark_system.start()
playsound(get_turf(src), 'sound/effects/pop.ogg', 50, FALSE)
return TRUE
/obj/item/pipe_dispenser/pre_attack(atom/A, mob/user)
if(!ISADVANCEDTOOLUSER(user) || istype(A, /turf/open/space/transit))
return ..()
var/atom/attack_target = A
//So that changing the menu settings doesn't affect the pipes already being built.
var/queued_p_type = recipe.id
var/queued_p_dir = p_dir
var/queued_p_flipped = p_flipped
//Unwrench pipe before we build one over/paint it, but only if we're not already running a do_after on it already to prevent a potential runtime.
if((mode & DESTROY_MODE) && (upgrade_flags & RPD_UPGRADE_UNWRENCH) && istype(attack_target, /obj/machinery/atmospherics) && !(DOING_INTERACTION_WITH_TARGET(user, attack_target)))
attack_target = attack_target.wrench_act(user, src)
if(!isatom(attack_target))
CRASH("When attempting to call [A.type].wrench_act(), received the following non-atom return value: [attack_target]")
//make sure what we're clicking is valid for the current category
var/static/list/make_pipe_whitelist
if(!make_pipe_whitelist)
make_pipe_whitelist = typecacheof(list(/obj/structure/lattice, /obj/structure/girder, /obj/item/pipe, /obj/structure/window, /obj/structure/grille))
if(istype(attack_target, /obj/machinery/atmospherics) && mode & BUILD_MODE)
attack_target = get_turf(attack_target)
var/can_make_pipe = (isturf(attack_target) || is_type_in_typecache(attack_target, make_pipe_whitelist))
. = TRUE
if((mode & DESTROY_MODE) && istype(attack_target, /obj/item/pipe) || istype(attack_target, /obj/structure/disposalconstruct) || istype(attack_target, /obj/structure/c_transit_tube) || istype(attack_target, /obj/structure/c_transit_tube_pod) || istype(attack_target, /obj/item/pipe_meter) || istype(attack_target, /obj/structure/disposalpipe/broken))
to_chat(user, span_notice("You start destroying a pipe..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(do_after(user, destroy_speed, target = attack_target))
activate()
qdel(attack_target)
return
if(mode & REPROGRAM_MODE)
// If this is a placed smart pipe, try to reprogram it
var/obj/machinery/atmospherics/pipe/smart/S = attack_target
if(istype(S))
if (S.dir == ALL_CARDINALS)
to_chat(user, span_warning("\The [S] has no unconnected directions!"))
return
var/old_init_dir = S.get_init_directions()
if (old_init_dir == p_init_dir)
to_chat(user, span_warning("\The [S] is already in this configuration!"))
return
// Check for differences in unconnected directions
var/target_differences = (p_init_dir ^ old_init_dir) & ~S.connections
if (!target_differences)
to_chat(user, span_warning("\The [S] is already in this configuration for its unconnected directions!"))
return
to_chat(user, span_notice("You start reprogramming \the [S]..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(!do_after(user, reprogram_speed, target = S))
return
// Something else could have changed the target's state while we were waiting in do_after
// Most of the edge cases don't matter, but atmos components being able to have live connections not described by initializable directions sounds like a headache at best and an exploit at worst
// Double check to make sure that nothing has changed. If anything we were about to change was connected during do_after, abort
if (target_differences & S.connections)
to_chat(user, span_warning("\The [src]'s screen flashes a warning: Can't configure a pipe in a currently connected direction."))
return
// Grab the current initializable directions, which may differ from old_init_dir if someone else was working on the same pipe at the same time
var/current_init_dir = S.get_init_directions()
// Access p_init_dir directly. The RPD can change target layer and initializable directions (though not pipe type or dir) while working to dispense and connect a component,
// and have it reflected in the final result. Reprogramming should be similarly consistent.
var/new_init_dir = (current_init_dir & ~target_differences) | (p_init_dir & target_differences)
// Don't make a smart pipe with only one connection
if (ISSTUB(new_init_dir))
to_chat(user, span_warning("\The [src]'s screen flashes a warning: Can't configure a pipe to only connect in one direction."))
return
S.set_init_directions(new_init_dir)
// We're now reconfigured.
// We can never disconnect from existing connections, but we can connect to previously unconnected directions, and should immediately do so
var/newly_permitted_connections = new_init_dir & ~current_init_dir
if(newly_permitted_connections)
// We're allowed to connect in new directions. Recompute our nodes
// Disconnect from everything that is currently connected
for (var/i in 1 to S.device_type)
// This is basically pipe.nullifyNode, but using it here would create a pitfall for others attempting to
// copy and paste disconnection code for other components. Welcome to the atmospherics subsystem
var/obj/machinery/atmospherics/node = S.nodes[i]
if (!node)
continue
node.disconnect(S)
S.nodes[i] = null
// Get our new connections
S.atmos_init()
// Connect to our new connections
for (var/obj/machinery/atmospherics/O in S.nodes)
O.atmos_init()
O.add_member(src)
SSair.add_to_rebuild_queue(S)
// Finally, update our internal state - update_pipe_icon also updates dir and connections
S.update_pipe_icon()
user.visible_message(span_notice("[user] reprograms the \the [S]."),span_notice("You reprogram \the [S]."))
return
// If this is an unplaced smart pipe, try to reprogram it
var/obj/item/pipe/quaternary/I = attack_target
if(istype(I) && ispath(I.pipe_type, /obj/machinery/atmospherics/pipe/smart))
// An unplaced pipe never has any existing connections, so just directly assign the new configuration
I.p_init_dir = p_init_dir
I.update()
if(mode & BUILD_MODE)
switch(category) //if we've gotten this var, the target is valid
if(ATMOS_CATEGORY) //Making pipes
if(!can_make_pipe)
return ..()
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if (recipe.type == /datum/pipe_info/meter)
to_chat(user, span_notice("You start building a meter..."))
if(do_after(user, atmos_build_speed, target = attack_target))
activate()
var/obj/item/pipe_meter/PM = new /obj/item/pipe_meter(get_turf(attack_target))
PM.setAttachLayer(piping_layer)
if(mode & WRENCH_MODE)
PM.wrench_act(user, src)
else
if(recipe.all_layers == FALSE && (piping_layer == 1 || piping_layer == 5))
to_chat(user, span_notice("You can't build this object on the layer..."))
return ..()
to_chat(user, span_notice("You start building a pipe..."))
if(do_after(user, atmos_build_speed, target = attack_target))
if(recipe.all_layers == FALSE && (piping_layer == 1 || piping_layer == 5))//double check to stop cheaters (and to not waste time waiting for something that can't be placed)
to_chat(user, span_notice("You can't build this object on the layer..."))
return ..()
activate()
var/obj/machinery/atmospherics/path = queued_p_type
var/pipe_item_type = initial(path.construction_type) || /obj/item/pipe
var/obj/item/pipe/pipe_type = new pipe_item_type(
get_turf(attack_target),
queued_p_type,
queued_p_dir,
null,
GLOB.pipe_paint_colors[paint_color],
ispath(queued_p_type, /obj/machinery/atmospherics/pipe/smart) ? p_init_dir : null,
)
if(queued_p_flipped && istype(pipe_type, /obj/item/pipe/trinary/flippable))
var/obj/item/pipe/trinary/flippable/F = pipe_type
F.flipped = queued_p_flipped
pipe_type.update()
pipe_type.add_fingerprint(usr)
pipe_type.set_piping_layer(piping_layer)
if(ispath(queued_p_type, /obj/machinery/atmospherics) && !ispath(queued_p_type, /obj/machinery/atmospherics/pipe/color_adapter))
pipe_type.add_atom_colour(GLOB.pipe_paint_colors[paint_color], FIXED_COLOUR_PRIORITY)
if(mode & WRENCH_MODE)
pipe_type.wrench_act(user, src)
if(DISPOSALS_CATEGORY) //Making disposals pipes
if(!can_make_pipe)
return ..()
attack_target = get_turf(attack_target)
if(isclosedturf(attack_target))
to_chat(user, span_warning("[src]'s error light flickers; there's something in the way!"))
return
to_chat(user, span_notice("You start building a disposals pipe..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(do_after(user, disposal_build_speed, target = attack_target))
var/obj/structure/disposalconstruct/C = new (attack_target, queued_p_type, queued_p_dir, queued_p_flipped)
if(!C.can_place())
to_chat(user, span_warning("There's not enough room to build that here!"))
qdel(C)
return
activate()
C.add_fingerprint(usr)
C.update_appearance()
if(mode & WRENCH_MODE)
C.wrench_act(user, src)
return
if(TRANSIT_CATEGORY) //Making transit tubes
if(!can_make_pipe)
return ..()
attack_target = get_turf(attack_target)
if(isclosedturf(attack_target))
to_chat(user, span_warning("[src]'s error light flickers; there's something in the way!"))
return
var/turf/target_turf = get_turf(attack_target)
if(target_turf.is_blocked_turf(exclude_mobs = TRUE))
to_chat(user, span_warning("[src]'s error light flickers; there's something in the way!"))
return
to_chat(user, span_notice("You start building a transit tube..."))
playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
if(do_after(user, transit_build_speed, target = attack_target))
activate()
if(queued_p_type == /obj/structure/c_transit_tube_pod)
var/obj/structure/c_transit_tube_pod/pod = new /obj/structure/c_transit_tube_pod(attack_target)
pod.add_fingerprint(usr)
if(mode & WRENCH_MODE)
pod.wrench_act(user, src)
else
var/obj/structure/c_transit_tube/tube = new queued_p_type(attack_target)
tube.setDir(queued_p_dir)
if(queued_p_flipped)
tube.setDir(turn(queued_p_dir, 45))
tube.SimpleRotateFlip()
tube.add_fingerprint(usr)
if(mode & WRENCH_MODE)
tube.wrench_act(user, src)
return
else
return ..()
/obj/item/pipe_dispenser/proc/activate()
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, TRUE)
/obj/item/pipe_dispenser/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)
piping_layer = min(PIPING_LAYER_MAX, piping_layer + 1)
else if(delta_y > 0)
piping_layer = max(PIPING_LAYER_MIN, piping_layer - 1)
else
return
SStgui.update_uis(src)
to_chat(source, span_notice("You set the layer to [piping_layer]."))
#undef ATMOS_CATEGORY
#undef DISPOSALS_CATEGORY
#undef TRANSIT_CATEGORY
#undef BUILD_MODE
#undef DESTROY_MODE
#undef WRENCH_MODE
#undef REPROGRAM_MODE
/obj/item/rpd_upgrade
name = "RPD advanced design disk"
desc = "It seems to be empty."
icon = 'icons/obj/module.dmi'
icon_state = "datadisk3"
/// Bitflags for upgrades
var/upgrade_flags
/obj/item/rpd_upgrade/unwrench
desc = "Adds reverse wrench mode to the RPD. Attention, due to budget cuts, the mode is hard linked to the destroy mode control button."
upgrade_flags = RPD_UPGRADE_UNWRENCH