diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index bf4ccff1395..1c1acb4a81d 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -79,7 +79,7 @@ #define COLOR_BLUE_GRAY "#75A2BB" #define COLOR_PINK "#FFC0CB" -#define COLOR_LIGHT_PINK "#ff3cc8" +#define COLOR_LIGHT_PINK "#FF3CC8" #define COLOR_SCIENCE_PINK "#C96DBF" #define COLOR_MOSTLY_PURE_PINK "#E4005B" #define COLOR_ADMIN_PINK "#D100D1" @@ -89,7 +89,7 @@ #define COLOR_STRONG_MAGENTA "#B800B8" #define COLOR_PURPLE "#800080" #define COLOR_VIOLET "#B900F7" -#define COLOR_STRONG_VIOLET "#6927c5" +#define COLOR_STRONG_VIOLET "#6927C5" #define COLOR_DARK_PURPLE "#551A8B" #define COLOR_ORANGE "#FF9900" diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index a471391ba0e..928ef2c9947 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -204,8 +204,12 @@ // Don't delete the stock part singletons for (var/atom/atom_part in component_parts) qdel(atom_part) - component_parts.Cut() + component_parts = null + + //delete any reference to cached stack parts created during display parts + QDEL_LIST_ASSOC_VAL(cached_stack_parts) + cached_stack_parts = null LAZYCLEARLIST(cached_stack_parts) @@ -356,6 +360,7 @@ set_occupant(null) circuit = null LAZYCLEARLIST(component_parts) + LAZYCLEARLIST(cached_stack_parts) /** * Drop every movable atom in the machine's contents list that is not a component_part. @@ -374,6 +379,9 @@ if(movable_atom in component_parts) continue + if(cached_stack_parts && cached_stack_parts[movable_atom.type]) + continue + movable_atom.forceMove(this_turf) if(occupant == movable_atom) diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm index 1863e68c7ea..32bd167fbd6 100644 --- a/code/game/objects/items/RPD.dm +++ b/code/game/objects/items/RPD.dm @@ -139,14 +139,19 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( 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)) + row["previews"] += list(list( + "selected" = dirtype == PIPE_ONEDIR ? TRUE : (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()) @@ -317,7 +322,6 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if(istype(target, /obj/machinery/air_sensor)) if(!do_after(user, destroy_speed, target)) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN - qdel(target) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN @@ -379,6 +383,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( "ducting_layer" = ducting_layer, "preview_rows" = recipe.get_preview(p_dir), "categories" = list(), + "selected_recipe" = recipe.name, "selected_color" = paint_color, "mode" = mode, ) @@ -404,7 +409,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if(GLOB.objects_by_id_tag[CHAMBER_SENSOR_FROM_ID(initial(sensor.chamber_id))] != null) continue - r += list(list("pipe_name" = info.name, "pipe_index" = i, "selected" = (info == recipe), "all_layers" = info.all_layers)) + r += list(list("pipe_name" = info.name, "pipe_index" = i)) if(info == recipe) data["selected_category"] = c if(r.len == 0) //when all air sensors are installed this list will become empty diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index 3e7024ae57f..3236236408b 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -20,6 +20,8 @@ resistance_flags = FIRE_PROOF max_integrity = 200 obj_flags = CAN_BE_HIT + armor_type = /datum/armor/machinery_atmospherics + ///Check if the object can be unwrenched var/can_unwrench = FALSE ///Bitflag of the initialized directions (NORTH | SOUTH | EAST | WEST) @@ -154,6 +156,47 @@ node_machine.disconnect(src) nodes[i] = null +/** + * Setter for device direction + * + * Set the direction to either SOUTH or WEST if the pipe_flag is set to PIPING_CARDINAL_AUTONORMALIZE, called in New(), used mostly by layer manifolds + */ +/obj/machinery/atmospherics/proc/normalize_cardinal_directions() + switch(dir) + if(SOUTH) + setDir(NORTH) + if(WEST) + setDir(EAST) + +/** + * setter for pipe layers + * + * Set the layer of the pipe that the device has to a new_layer + * Arguments: + * * new_layer - the layer at which we want the piping_layer to be (1 to 5) + */ +/obj/machinery/atmospherics/proc/set_piping_layer(new_layer) + piping_layer = (pipe_flags & PIPING_DEFAULT_LAYER_ONLY) ? PIPING_LAYER_DEFAULT : new_layer + update_appearance() + +/obj/machinery/atmospherics/update_icon() + . = ..() + update_layer() + +/** + * Find a connecting /obj/machinery/atmospherics in specified direction, called by relaymove() + * used by ventcrawling mobs to check if they can move inside a pipe in a specific direction + * Arguments: + * * direction - the direction we are checking against + * * prompted_layer - the piping_layer we are inside + */ +/obj/machinery/atmospherics/proc/find_connecting(direction, prompted_layer) + for(var/obj/machinery/atmospherics/target in get_step_multiz(src, direction)) + if(!(target.initialize_directions & get_dir(target,src)) && !istype(target, /obj/machinery/atmospherics/pipe/multiz)) + continue + if(connection_check(target, prompted_layer)) + return target + /** * Getter for node_connects * @@ -175,18 +218,6 @@ return node_connects -/** - * Setter for device direction - * - * Set the direction to either SOUTH or WEST if the pipe_flag is set to PIPING_CARDINAL_AUTONORMALIZE, called in New(), used mostly by layer manifolds - */ -/obj/machinery/atmospherics/proc/normalize_cardinal_directions() - switch(dir) - if(SOUTH) - setDir(NORTH) - if(WEST) - setDir(EAST) - /** * Initialize for atmos devices * @@ -200,50 +231,21 @@ for(var/i in 1 to device_type) for(var/obj/machinery/atmospherics/target in get_step(src,node_connects[i])) - if(can_be_node(target, i)) + if(can_be_node(target)) nodes[i] = target break - update_appearance() -/** - * setter for pipe layers - * - * Set the layer of the pipe that the device has to a new_layer - * Arguments: - * * new_layer - the layer at which we want the piping_layer to be (1 to 5) - */ -/obj/machinery/atmospherics/proc/set_piping_layer(new_layer) - piping_layer = (pipe_flags & PIPING_DEFAULT_LAYER_ONLY) ? PIPING_LAYER_DEFAULT : new_layer update_appearance() -/obj/machinery/atmospherics/update_icon() - . = ..() - update_layer() - /** * Check if a node can actually exists by connecting to another machine * called on atmos_init() * Arguments: * * obj/machinery/atmospherics/target - the machine we are connecting to - * * iteration - the current node we are checking (from 1 to 4) */ -/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, iteration) +/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target) return connection_check(target, piping_layer) -/** - * Find a connecting /obj/machinery/atmospherics in specified direction, called by relaymove() - * used by ventcrawling mobs to check if they can move inside a pipe in a specific direction - * Arguments: - * * direction - the direction we are checking against - * * prompted_layer - the piping_layer we are inside - */ -/obj/machinery/atmospherics/proc/find_connecting(direction, prompted_layer) - for(var/obj/machinery/atmospherics/target in get_step_multiz(src, direction)) - if(!(target.initialize_directions & get_dir(target,src)) && !istype(target, /obj/machinery/atmospherics/pipe/multiz)) - continue - if(connection_check(target, prompted_layer)) - return target - /** * Check the connection between two nodes * @@ -254,20 +256,15 @@ * * given_layer - the piping_layer we are checking */ /obj/machinery/atmospherics/proc/connection_check(obj/machinery/atmospherics/target, given_layer) - if(is_connectable(target, given_layer) && target.is_connectable(src, given_layer) && check_init_directions(target)) - return TRUE - return FALSE + //if target is not multiz then we have to check if the target & src connect in the same direction + if(!istype(target, /obj/machinery/atmospherics/pipe/multiz) && !((initialize_directions & get_dir(src, target)) && (target.initialize_directions & get_dir(target, src)))) + return FALSE -/** - * check if the initialized direction are the same on both sides (or if is a multiz adapter) - * returns TRUE or FALSE if the connection is possible or not - * Arguments: - * * obj/machinery/atmospherics/target - the machinery we want to connect to - */ -/obj/machinery/atmospherics/proc/check_init_directions(obj/machinery/atmospherics/target) - if((initialize_directions & get_dir(src, target) && target.initialize_directions & get_dir(target,src)) || istype(target, /obj/machinery/atmospherics/pipe/multiz)) - return TRUE - return FALSE + //both target & src can't be connected either way + if(!is_connectable(target, given_layer) || !target.is_connectable(src, given_layer)) + return FALSE + + return TRUE /** * check if the piping layer and color are the same on both sides (grey can connect to all colors) @@ -279,33 +276,20 @@ /obj/machinery/atmospherics/proc/is_connectable(obj/machinery/atmospherics/target, given_layer) if(isnull(given_layer)) given_layer = piping_layer - if(check_connectable_layer(target, given_layer) && target.loc != loc && check_connectable_color(target)) - return TRUE - return FALSE -/** - * check if the piping layer are the same on both sides or one of them has the PIPING_ALL_LAYER flag - * returns TRUE if one of the parameters is TRUE - * called by is_connectable() - * Arguments: - * * obj/machinery/atmospherics/target - the machinery we want to connect to - * * given_layer - the piping_layer we are connecting to - */ -/obj/machinery/atmospherics/proc/check_connectable_layer(obj/machinery/atmospherics/target, given_layer) - if(target.piping_layer == given_layer || target.pipe_flags & PIPING_ALL_LAYER) - return TRUE - return FALSE + // you cant place the machine on the same location as the target cause it blocks + if(target.loc == loc) + return FALSE -/** - * check if the color are the same on both sides or if one of the pipes are grey or have the PIPING_ALL_COLORS flag - * returns TRUE if one of the parameters is TRUE - * Arguments: - * * obj/machinery/atmospherics/target - the machinery we want to connect to - */ -/obj/machinery/atmospherics/proc/check_connectable_color(obj/machinery/atmospherics/target) - if(target.pipe_color == pipe_color || ((target.pipe_flags | pipe_flags) & PIPING_ALL_COLORS) || target.pipe_color == COLOR_VERY_LIGHT_GRAY || pipe_color == COLOR_VERY_LIGHT_GRAY) - return TRUE - return FALSE + //if the target is not in the same piping layer & it does not have the all layer connection flag[which allows it to be connected regardless of layer] then we are out + if(target.piping_layer != given_layer && !(target.pipe_flags & PIPING_ALL_LAYER)) + return FALSE + + //if the target does not have the same color and it does not have all color connection flag[which allows it to be connected regardless of color] & one of the pipes is not gray[allowing for connection regardless] then we are out + if(target.pipe_color != pipe_color && !((target.pipe_flags | pipe_flags) & PIPING_ALL_COLORS) && target.pipe_color != COLOR_VERY_LIGHT_GRAY && pipe_color != COLOR_VERY_LIGHT_GRAY) + return FALSE + + return TRUE /** * Called on construction and when expanding the datum_pipeline, returns the nodes of the device diff --git a/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm b/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm index 597af01f6a0..9775e315637 100644 --- a/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm +++ b/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm @@ -3,6 +3,7 @@ /obj/machinery/atmospherics/pipe/heat_exchanging/manifold icon = 'icons/obj/atmospherics/pipes/he-manifold.dmi' icon_state = "manifold-3" + base_icon_state = "manifold" name = "pipe manifold" desc = "A manifold composed of regular pipes." @@ -19,6 +20,9 @@ initialize_directions = ALL_CARDINALS initialize_directions &= ~dir +/obj/machinery/atmospherics/pipe/heat_exchanging/manifold/update_pipe_icon() + icon_state = "[base_icon_state]-[piping_layer]" + /obj/machinery/atmospherics/pipe/heat_exchanging/manifold/update_overlays() . = ..() var/mutable_appearance/center = mutable_appearance(icon, "manifold_center") diff --git a/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold4w.dm b/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold4w.dm index 4f573b2ce21..effce654f11 100644 --- a/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold4w.dm +++ b/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold4w.dm @@ -3,6 +3,7 @@ /obj/machinery/atmospherics/pipe/heat_exchanging/manifold4w icon = 'icons/obj/atmospherics/pipes/he-manifold.dmi' icon_state = "manifold4w-3" + base_icon_state = "manifold4w" name = "4-way pipe manifold" desc = "A manifold composed of heat-exchanging pipes." @@ -17,6 +18,9 @@ /obj/machinery/atmospherics/pipe/heat_exchanging/manifold4w/set_init_directions() initialize_directions = initial(initialize_directions) +/obj/machinery/atmospherics/pipe/heat_exchanging/manifold4w/update_pipe_icon() + icon_state = "[base_icon_state]-[piping_layer]" + /obj/machinery/atmospherics/pipe/heat_exchanging/manifold4w/update_overlays() . = ..() var/mutable_appearance/center = mutable_appearance(icon, "manifold4w_center") diff --git a/code/modules/atmospherics/machinery/pipes/smart.dm b/code/modules/atmospherics/machinery/pipes/smart.dm index 5099aeefc7b..f5b416f44ef 100644 --- a/code/modules/atmospherics/machinery/pipes/smart.dm +++ b/code/modules/atmospherics/machinery/pipes/smart.dm @@ -12,40 +12,76 @@ GLOBAL_LIST_INIT(atmos_components, typecacheof(list(/obj/machinery/atmospherics) pipe_state = "manifold4w" ///Current active connections var/connections = NONE + ///Was this pipe created during map load + var/map_loaded_pipe = FALSE + +/obj/machinery/atmospherics/pipe/smart/Initialize(mapload) + map_loaded_pipe = mapload + return ..() + +///helper function to append all directions into an single bit flag +/obj/machinery/atmospherics/pipe/smart/proc/append_directions(list/spanning_directions) + var/bit_flag = NONE + for(var/i in 1 to length(spanning_directions)) + var/spanning_direction = spanning_directions[i] + if(!spanning_direction) + continue + bit_flag |= spanning_direction + return bit_flag /obj/machinery/atmospherics/pipe/smart/update_pipe_icon() icon = 'icons/obj/atmospherics/pipes/pipes_bitmask.dmi' - connections = NONE + //find all directions this pipe is connected with other nodes + connections = NONE for(var/i in 1 to device_type) if(!nodes[i]) continue var/obj/machinery/atmospherics/node = nodes[i] var/connected_dir = get_dir(src, node) connections |= connected_dir - var/bitfield = CARDINAL_TO_FULLPIPES(connections) - dir = check_binary_direction(connections) + //set the correct direction for this node in case of binary directions + switch(connections) + if(EAST | WEST) + dir = EAST + if(SOUTH | NORTH) + dir = SOUTH + else + dir = connections - // If we dont have enough bits to make a proper sprite, add some shortpipe bits + //same as connections but used for spriting + var/sprite_bits = NONE + //the directions this pipe stretches out in e.g. T pipe is EAST,WEST & SOUTH, L pipe is NORTH,EAST & so on + var/list/spanning_directions = get_node_connects() + /** + *For pipes created during mapload we draw the pipes sprite only in directions where its connected to a machine + *so for example if an T shaped pipe is connected only in its EAST & WEST directions then only those ends are drawn + *but the SOUTH end is not drawn + *this will allow mappers to use whatever pipes but the end result has no visual clutter. + *This is actually just an bandage for lazy mappers using + pipes all over the place without carying about directions so hopefully when they map pipes correctly we can remove this + */ + if(map_loaded_pipe) + sprite_bits = connections + /** + * if pipe is connected in only one direction[e.g. after disconnecting its neighbour] then to avoid a broken sprite append the reverse direction of its one connected end. + * this wont work for L pipes because if one of its ends is broken then the opposite direction of any of its last connected end is invalid + * e.g. for an L pipe if the top[NORTH] end is broken the opposite of its one remaining connected end[i.e EAST END] is WEST but thats not an valid direction for this pipe + * so we have to again check one last time after this to make sure the pipe isnt broken + */ + if(ISSTUB(sprite_bits)) + // & initialize_directions will yield 0 if the reversed direction is not valid + sprite_bits |= REVERSE_DIR(sprite_bits) & get_init_directions() + //if its still broken after the above patch then screw it we make the pipe an normal non mapload type and do the usual stuff with player created pipes + if(ISSTUB(sprite_bits)) + sprite_bits = append_directions(spanning_directions) + /** + *for pipes created by players during the round we draw the pipe in all directions so they + *can visually see what ends are free. + */ + else + sprite_bits = append_directions(spanning_directions) - // Smart pipe icons differ from classic pipe icons in that we stop adding - // short pipe directions as soon as we find a valid sprite, rather than - // adding in all connectable directions. - // This prevents a lot of visual clutter, though it does make it harder to - // notice completely disconnected pipes. - if(ISSTUB(connections)) - var/bits_to_add = NONE - if(connections != NONE) - bits_to_add |= REVERSE_DIR(connections) & initialize_directions - var/candidates = initialize_directions - var/shift = 0 - // Note that candidates "should" never reach 0, as stub pipes are not allowed and break things - while (ISSTUB(connections | bits_to_add) && (candidates >> shift) != 0) - bits_to_add |= candidates & (1 << shift) - shift += 1 - bitfield |= CARDINAL_TO_SHORTPIPES(bits_to_add) - - icon_state = "[bitfield]_[piping_layer]" + icon_state = "[sprite_bits]_[piping_layer]" /obj/machinery/atmospherics/pipe/smart/set_init_directions(init_dir) if(init_dir) @@ -53,28 +89,6 @@ GLOBAL_LIST_INIT(atmos_components, typecacheof(list(/obj/machinery/atmospherics) else initialize_directions = ALL_CARDINALS -/obj/machinery/atmospherics/pipe/smart/proc/check_binary_direction(direction) - switch(direction) - if(EAST|WEST) - return EAST - if(SOUTH|NORTH) - return SOUTH - else - return direction - -/obj/machinery/atmospherics/pipe/smart/proc/check_manifold_direction(direction) - switch(direction) - if(NORTH|SOUTH|EAST) - return WEST - if(NORTH|SOUTH|WEST) - return EAST - if(NORTH|WEST|EAST) - return SOUTH - if(SOUTH|WEST|EAST) - return NORTH - else - return null - //mapping helpers /obj/machinery/atmospherics/pipe/smart/simple icon = 'icons/obj/atmospherics/pipes/simple.dmi' diff --git a/tgui/packages/tgui/interfaces/RapidPipeDispenser.js b/tgui/packages/tgui/interfaces/RapidPipeDispenser.js index 9cccb960cdd..5f4be4bc4d8 100644 --- a/tgui/packages/tgui/interfaces/RapidPipeDispenser.js +++ b/tgui/packages/tgui/interfaces/RapidPipeDispenser.js @@ -193,7 +193,7 @@ const LayerSection = (props, context) => { const PipeTypeSection = (props, context) => { const { act, data } = useBackend(context); const { categories = [] } = data; - const { selected_category } = data; + const { selected_category, selected_recipe } = data; const [categoryName, setCategoryName] = useLocalState( context, 'categoryName', @@ -221,7 +221,7 @@ const PipeTypeSection = (props, context) => { key={recipe.pipe_index} fluid ellipsis - checked={recipe.selected} + checked={recipe.pipe_name === selected_recipe} content={recipe.pipe_name} title={recipe.pipe_name} onClick={() =>