Files
SmArtKar cc34f01082 Fixes plumbing ducts having weird shadows (#96102)
## About The Pull Request

UPDATE_ICON is needed rather than UPDATE_ICON_STATE to update the
emissive blocker
Closes #96076

## Changelog
🆑
fix: Fixed plumbing ducts having weird shadows
/🆑
2026-05-23 17:17:12 +02:00

348 lines
10 KiB
Plaintext

/obj/machinery/duct
name = "fluid duct"
icon = 'icons/obj/pipes_n_cables/hydrochem/fluid_ducts.dmi'
icon_state = "nduct"
layer = PLUMBING_PIPE_VISIBILE_LAYER
use_power = NO_POWER_USE
///our ductnet, wich tracks what we're connected to
var/datum/ductnet/net
///the color of our duct
var/duct_color = ATMOS_COLOR_OMNI
///the layer of the duct
var/duct_layer = DUCT_LAYER_DEFAULT
///track machines we're connected to.
var/list/atom/movable/neighbours
/obj/machinery/duct/Initialize(mapload, color_of_duct, layer_of_duct)
if(GLOB.plumbing_layer_names["[layer_of_duct]"])
duct_layer = layer_of_duct
if(PERFORM_ALL_TESTS(maptest_log_mapping))
var/datum/overlap = ducting_layer_check(src, duct_layer)
if(!isnull(overlap))
var/message = GLOB.plumbing_layer_names["[duct_layer]"]
if(istype(overlap, /obj/machinery/duct))
message = "duct on [message]"
else
message = "machine on [message]"
log_mapping("Overlapping plumbing [message] detected at [AREACOORD(src)]")
return INITIALIZE_HINT_QDEL
. = ..()
if(GLOB.pipe_color_name[color_of_duct])
duct_color = color_of_duct
add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY)
var/offset
switch(duct_layer)
if(FIRST_DUCT_LAYER)
offset = -10
if(SECOND_DUCT_LAYER)
offset = -5
if(THIRD_DUCT_LAYER)
offset = 0
if(FOURTH_DUCT_LAYER)
offset = 5
if(FIFTH_DUCT_LAYER)
offset = 10
pixel_x = offset
pixel_y = offset
layer = initial(layer) + duct_layer * 0.0003
AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE)
register_context()
/obj/machinery/duct/post_machine_initialize()
. = ..()
//can be initialized already during map init by its neighbours
if(!net)
net = new (src)
LAZYINITLIST(neighbours)
for(var/direction in GLOB.cardinals)
//we have already connected. happens in circular connections
var/connected = FALSE
for(var/atom/movable/neighbour as anything in neighbours)
if(direction & neighbours[neighbour])
connected = TRUE
break
if(connected)
continue
for(var/atom/movable/plumbable in get_step(src, direction))
var/opposite_dir = REVERSE_DIR(direction)
if(istype(plumbable, /obj/machinery/duct))
var/obj/machinery/duct/other = plumbable
//must be same duct color
if((duct_color != other.duct_color) && !(duct_color == ATMOS_COLOR_OMNI || other.duct_color == ATMOS_COLOR_OMNI))
continue
//must be same duct layer
if(!(duct_layer & other.duct_layer))
continue
//connect ductnets
if(!other.net) //will be null only for map loaded ducts
net.ducts += other
other.net = net
else if(net != other.net) //merge the nets
var/datum/ductnet/othernet = other.net
//Take all its suppliers & demanders
net.suppliers |= othernet.suppliers
net.demanders |= othernet.demanders
for(var/datum/component/plumbing/component as anything in othernet.suppliers + othernet.demanders)
for(var/obj/machinery/duct/duct as anything in component.ducts)
if(component.ducts[duct] == othernet)
component.ducts[duct] = net
othernet.suppliers.Cut()
othernet.demanders.Cut()
//Take all its ducts
net.ducts |= othernet.ducts
for(var/obj/machinery/duct/duct as anything in othernet.ducts)
duct.net = net
//destory it
qdel(othernet)
//connecting us to duct
neighbours[other] = direction
//connecting duct to us
LAZYADDASSOC(other.neighbours, src, opposite_dir)
other.update_appearance(UPDATE_ICON)
continue
for(var/datum/component/plumbing/plumbing as anything in plumbable.GetComponents(/datum/component/plumbing))
//not anchored
if(!plumbing.active())
continue
//not on the same layer
if(!(duct_layer & plumbing.ducting_layer))
continue
//does the duct backend connect to either supplier or demander
if(!(opposite_dir & (plumbing.supply_connects | plumbing.demand_connects)))
continue
//connect duct to plumber
if(!net.add_plumber(plumbing, opposite_dir))
continue
//assign neighbour
neighbours[plumbing.parent] = direction
update_appearance(UPDATE_ICON)
///we disconnect ourself from our neighbours. we also destroy our ductnet and tell our neighbours to make a new one
/obj/machinery/duct/on_deconstruction()
var/obj/item/stack/ducts/duct_stack = new (drop_location())
duct_stack.duct_color = duct_color
duct_stack.duct_layer = duct_layer
duct_stack.add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY)
///Removes duct from ductnet
/obj/machinery/duct/proc/disconnect()
//remove ourself from the duct
net.ducts -= src
if(!net.ducts.len)
qdel(net) //destroy the pipeline. Suppliers aren't important if there aren't any ducts left
net = null
/obj/machinery/duct/Destroy()
//object was early deleted
if(!net)
return ..()
var/list/atom/movable/visited = list(src = TRUE)
while(neighbours.len)
var/atom/movable/neighbour = popleft(neighbours)
//disconnect ourself from our neighbours
var/obj/machinery/duct/pipe = neighbour
if(istype(pipe))
pipe.neighbours -= src
pipe.update_appearance(UPDATE_ICON)
//find every node that can be reached from our neighbour making sure to not revisit it again in circles
if(visited[neighbour])
continue
var/datum/ductnet/newnet
var/list/atom/movable/queue = list(neighbour)
while(queue.len)
var/atom/movable/node = popleft(queue)
if(visited[node])
continue
//visit all neighbours of this pipe as well
pipe = node
if(istype(pipe))
//assign to new pipenet
pipe.disconnect()
if(!newnet)
newnet = new
newnet.ducts += pipe
pipe.net = newnet
//go through its neighbours as well
for(var/atom/movable/subnode in pipe.neighbours)
queue += subnode
visited[node] = TRUE
continue
//assign machines to new network
for(var/datum/component/plumbing/plumbing as anything in node.GetComponents(/datum/component/plumbing))
//disconnect old net
for(var/dirtext in plumbing.ducts)
if(plumbing.ducts[dirtext] == net)
net.remove_plumber(plumbing)
//assign new net
if(newnet)
for(pipe as anything in newnet.ducts)
var/dir = pipe.neighbours[node]
if(dir)
newnet.add_plumber(plumbing, REVERSE_DIR(dir))
disconnect()
return ..()
/obj/machinery/duct/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = NONE
if(held_item?.tool_behaviour == TOOL_WRENCH)
context[SCREENTIP_CONTEXT_LMB] = "Destroy duct"
return CONTEXTUAL_SCREENTIP_SET
/obj/machinery/duct/examine(mob/user)
. = ..()
. += span_notice("Its current color and layer are [GLOB.pipe_color_name[duct_color]] and [GLOB.plumbing_layer_names["[duct_layer]"]]. Use in-hand to change.")
. += span_notice("It can be [EXAMINE_HINT("wrenched")] apart.")
/obj/machinery/duct/update_icon_state()
var/temp_icon = initial(icon_state)
//compute connections
var/connects = NONE
for(var/neighbour in neighbours)
connects |= neighbours[neighbour]
for(var/direction in GLOB.cardinals)
switch(direction & connects)
if(NORTH)
temp_icon += "_n"
if(SOUTH)
temp_icon += "_s"
if(EAST)
temp_icon += "_e"
if(WEST)
temp_icon += "_w"
icon_state = temp_icon
return ..()
/obj/machinery/duct/wrench_act(mob/living/user, obj/item/wrench) //I can also be the RPD
wrench.play_tool_sound(src)
user.visible_message( \
"[user] ununfastens \the [src].", \
span_notice("You unfasten \the [src]."), \
span_hear("You hear ratcheting."))
deconstruct()
return ITEM_INTERACT_SUCCESS
/obj/item/stack/ducts
name = "stack of duct"
desc = "A stack of fluid ducts."
singular_name = "duct"
icon = 'icons/obj/pipes_n_cables/hydrochem/fluid_ducts.dmi'
icon_state = "ducts"
mats_per_unit = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*5)
w_class = WEIGHT_CLASS_TINY
novariants = FALSE
max_amount = 50
item_flags = NOBLUDGEON
merge_type = /obj/item/stack/ducts
matter_amount = 1
///Color of our duct
var/duct_color = ATMOS_COLOR_OMNI
///Default layer of our duct
var/duct_layer = THIRD_DUCT_LAYER
/obj/item/stack/ducts/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
//when wrench is over us
register_context()
//when we are over pipe
register_item_context()
/obj/item/stack/ducts/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = NONE
if(held_item?.tool_behaviour == TOOL_WRENCH && isopenturf(loc))
context[SCREENTIP_CONTEXT_LMB] = "Wrench duct"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/stack/ducts/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
. = NONE
if(istype(target, /obj/machinery/duct))
context[SCREENTIP_CONTEXT_LMB] = "Pick duct"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/stack/ducts/examine(mob/user)
. = ..()
. += span_notice("Its current color and layer are [GLOB.pipe_color_name[duct_color]] and [GLOB.plumbing_layer_names["[duct_layer]"]]. Use in-hand to change.")
. += span_notice("Place on ground & [EXAMINE_HINT("wrench")] to create duct.")
/obj/item/stack/ducts/attack_self(mob/user)
var/new_layer = tgui_input_list(user, "Select a layer", "Layer", GLOB.plumbing_layers, GLOB.plumbing_layer_names["[duct_layer]"])
if(!user.is_holding(src))
return
if(new_layer)
duct_layer = GLOB.plumbing_layers[new_layer]
var/new_color = tgui_input_list(user, "Select a color", "Color", GLOB.pipe_paint_colors, GLOB.pipe_color_name[duct_color])
if(!user.is_holding(src))
return
if(new_color)
duct_color = GLOB.pipe_paint_colors[new_color]
add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY)
/obj/item/stack/ducts/wrench_act(mob/living/user, obj/item/tool)
return check_attach_turf(loc)
/obj/item/stack/ducts/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
// Turn into a duct stack and then merge to the in-hand stack.
if(istype(interacting_with, /obj/machinery/duct))
if(amount == max_amount)
balloon_alert(user, "stack full!")
return ITEM_INTERACT_FAILURE
qdel(interacting_with)
add(1)
return ITEM_INTERACT_SUCCESS
return check_attach_turf(interacting_with, user)
/obj/item/stack/ducts/proc/check_attach_turf(turf/open_turf, mob/user)
. = NONE
if(isopenturf(open_turf))
var/datum/overlap = ducting_layer_check(open_turf, duct_layer)
if(!isnull(overlap))
if(user)
open_turf.balloon_alert(user, "overlapping [istype(overlap, /obj/machinery/duct) ? "duct" : "machine"] detected!")
return ITEM_INTERACT_FAILURE
new /obj/machinery/duct(open_turf, duct_color, duct_layer)
playsound(open_turf, 'sound/machines/click.ogg', 50, TRUE)
use(1)
return ITEM_INTERACT_SUCCESS
/obj/item/stack/ducts/fifty
amount = 50