diff --git a/code/datums/elements/connect_loc.dm b/code/datums/elements/connect_loc.dm
new file mode 100644
index 0000000000..fee9072f75
--- /dev/null
+++ b/code/datums/elements/connect_loc.dm
@@ -0,0 +1,43 @@
+/// This element hooks a signal onto the loc the current object is on.
+/// When the object moves, it will unhook the signal and rehook it to the new object.
+/datum/element/connect_loc
+ element_flags = ELEMENT_BESPOKE
+ id_arg_index = 2
+
+ /// An assoc list of signal -> procpath to register to the loc this object is on.
+ var/list/connections
+
+/datum/element/connect_loc/Attach(atom/movable/listener, list/connections)
+ . = ..()
+ if (!istype(listener))
+ return ELEMENT_INCOMPATIBLE
+
+ src.connections = connections
+
+ RegisterSignal(listener, COMSIG_MOVABLE_MOVED, .proc/on_moved, override = TRUE)
+ update_signals(listener)
+
+/datum/element/connect_loc/Detach(atom/movable/listener)
+ . = ..()
+ unregister_signals(listener, listener.loc)
+ UnregisterSignal(listener, COMSIG_MOVABLE_MOVED)
+
+/datum/element/connect_loc/proc/update_signals(atom/movable/listener)
+ var/atom/listener_loc = listener.loc
+ if(isnull(listener_loc))
+ return
+
+ for (var/signal in connections)
+ //override=TRUE because more than one connect_loc element instance tracked object can be on the same loc
+ listener.RegisterSignal(listener_loc, signal, connections[signal], override=TRUE)
+
+/datum/element/connect_loc/proc/unregister_signals(datum/listener, atom/old_loc)
+ if(isnull(old_loc))
+ return
+
+ listener.UnregisterSignal(old_loc, connections)
+
+/datum/element/connect_loc/proc/on_moved(atom/movable/listener, atom/old_loc)
+ SIGNAL_HANDLER
+ unregister_signals(listener, old_loc)
+ update_signals(listener)
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index a21b026080..b3ad29e637 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -308,13 +308,32 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
S.update_icon()
CHECK_TICK
-/obj/machinery/conveyor_switch/attackby(obj/item/I, mob/user, params)
- if(I.tool_behaviour == TOOL_CROWBAR)
- var/obj/item/conveyor_switch_construct/C = new/obj/item/conveyor_switch_construct(src.loc)
- C.id = id
- transfer_fingerprints_to(C)
- to_chat(user, "You detach the conveyor switch.")
- qdel(src)
+/obj/machinery/conveyor_switch/crowbar_act(mob/user, obj/item/tool)
+ tool.play_tool_sound(src, 50)
+ var/obj/item/conveyor_switch_construct/switch_construct = new/obj/item/conveyor_switch_construct(src.loc)
+ switch_construct.id = id
+ transfer_fingerprints_to(switch_construct)
+ to_chat(user, span_notice("You detach [src]."))
+ qdel(src)
+ return TRUE
+
+/obj/machinery/conveyor_switch/screwdriver_act(mob/user, obj/item/tool)
+ tool.play_tool_sound(src, 50)
+ oneway = !oneway
+ to_chat(user, span_notice("You set [src] to [oneway ? "one way" : "default"] configuration."))
+ return TRUE
+
+/obj/machinery/conveyor_switch/wrench_act(mob/user, obj/item/tool)
+ tool.play_tool_sound(src, 50)
+ invert_icon = !invert_icon
+ update_appearance()
+ to_chat(user, span_notice("You set [src] to [invert_icon ? "inverted": "normal"] position."))
+ return TRUE
+
+/obj/machinery/conveyor_switch/examine(mob/user)
+ . = ..()
+ . += span_notice("[src] is set to [oneway ? "one way" : "default"] configuration. It can be changed with a screwdriver.")
+ . += span_notice("[src] is set to [invert_icon ? "inverted": "normal"] position. It can be rotated with a wrench.")
/obj/machinery/conveyor_switch/oneway
icon_state = "conveyor_switch_oneway"
diff --git a/code/modules/recycling/conveyor_sorter.dm b/code/modules/recycling/conveyor_sorter.dm
new file mode 100644
index 0000000000..0e42c79067
--- /dev/null
+++ b/code/modules/recycling/conveyor_sorter.dm
@@ -0,0 +1,144 @@
+// credits to jjpark-kb, see Skyrat-SS13/Skyrat-tg#8790 and related files
+// - hatterhat
+
+/obj/item/conveyor_sorter
+ name = "conveyor sorter lister"
+ desc = "A tool that is used to create conveyor sorting marks, and give sorting lists to them."
+ icon = 'icons/obj/recycling.dmi'
+ icon_state = "lister"
+ ///the list of conveyor sorters spawned by
+ var/list/spawned_sorters = list()
+ ///the list of things that are currently within the sorting list
+ var/list/current_sort = list()
+
+/obj/item/conveyor_sorter/Destroy()
+ for(var/deleting_sorters in spawned_sorters)
+ qdel(deleting_sorters)
+ return ..()
+
+/obj/item/conveyor_sorter/examine(mob/user)
+ . = ..()
+ . += span_notice("Use it to place down a conveyor sorter, up to three.")
+ . += span_notice("Use Alt-Click to reset the sorting list.")
+ . += span_notice("Attack things to attempt to add to the sorting list.")
+
+/obj/item/conveyor_sorter/attack_self(mob/user, modifiers)
+ if(length(spawned_sorters) >= 3)
+ to_chat(user, span_warning("You may only have three spawned conveyor sorters!"))
+ return
+ var/obj/effect/decal/cleanable/conveyor_sorter/new_cs = new /obj/effect/decal/cleanable/conveyor_sorter(get_turf(src))
+ new_cs.parent_item = src
+ spawned_sorters += new_cs
+
+/obj/item/conveyor_sorter/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ if(target == src)
+ return ..()
+ if(!proximity_flag)
+ return ..()
+ if(!ismovable(target))
+ return ..()
+ if(istype(target, /obj/effect/decal/cleanable/conveyor_sorter))
+ return
+ if(is_type_in_list(target, current_sort))
+ to_chat(user, span_warning("[target] is already in [src]'s sorting list!"))
+ return
+ if(length(current_sort) >= 5)
+ to_chat(user, span_warning("[src] already has five things within the sorting list!"))
+ return
+ current_sort += target.type
+ to_chat(user, span_notice("[target] has been added to [src]'s sorting list."))
+
+/obj/item/conveyor_sorter/AltClick(mob/user)
+ visible_message("[src] pings, resetting its sorting list!")
+ playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
+ current_sort = list()
+
+/obj/effect/decal/cleanable/conveyor_sorter
+ name = "conveyor sorter"
+ desc = "A mark that sorts items out based on what they are."
+ icon = 'icons/obj/recycling.dmi'
+ icon_state = "sorter"
+ layer = OBJ_LAYER
+ plane = GAME_PLANE
+ ///the list of items that will be sorted to the sorted direction
+ var/list/sorting_list = list()
+ //the direction that the items in the sorting list will be moved to
+ dir = NORTH
+ ///the parent conveyor sorter lister item, used for deletion
+ var/obj/item/conveyor_sorter/parent_item
+ /// To prevent spam
+ COOLDOWN_DECLARE(use_cooldown)
+
+ light_range = 3
+ light_color = COLOR_RED_LIGHT
+
+/obj/effect/decal/cleanable/conveyor_sorter/Initialize(mapload, list/datum/disease/diseases)
+ . = ..()
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = .proc/on_entered,
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+
+/obj/effect/decal/cleanable/conveyor_sorter/Destroy()
+ if(parent_item)
+ parent_item.spawned_sorters -= src
+ parent_item = null
+ return ..()
+
+/obj/effect/decal/cleanable/conveyor_sorter/examine(mob/user)
+ . = ..()
+ . += span_notice("Attack with conveyor sorter lister to set the sorting list.")
+ . += span_notice("Slap with empty hands to change the sorting direction.")
+ . += span_notice("Alt-Click to reset the sorting list.")
+ . += span_notice("Ctrl-Click to remove.")
+
+/obj/effect/decal/cleanable/conveyor_sorter/attack_hand(mob/living/user, list/modifiers)
+ var/user_choice = tgui_input_list(user, "Choose which direction to sort to!", "Direction choice", list("North", "East", "South", "West"))
+ if(!user_choice)
+ return ..()
+ switch(user_choice)
+ if("North")
+ setDir(NORTH)
+ if("East")
+ setDir(EAST)
+ if("South")
+ setDir(SOUTH)
+ if("West")
+ setDir(WEST)
+ visible_message("[src] pings, updating its sorting direction!")
+ playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
+
+/obj/effect/decal/cleanable/conveyor_sorter/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/conveyor_sorter))
+ var/obj/item/conveyor_sorter/cs_item = W
+ sorting_list = cs_item.current_sort
+ visible_message("[src] pings, updating its sorting list!")
+ playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
+ return
+ else
+ return ..()
+
+/obj/effect/decal/cleanable/conveyor_sorter/AltClick(mob/user)
+ visible_message("[src] pings, resetting its sorting list!")
+ playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
+ sorting_list = list()
+
+/obj/effect/decal/cleanable/conveyor_sorter/CtrlClick(mob/user)
+ visible_message("[src] pings, before disappearing!")
+ playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
+ qdel(src)
+
+/obj/effect/decal/cleanable/conveyor_sorter/proc/on_entered(datum/source, atom/movable/AM)
+ if(is_type_in_list(AM, sorting_list) && !AM.anchored && COOLDOWN_FINISHED(src, use_cooldown))
+ COOLDOWN_START(src, use_cooldown, 1 SECONDS)
+ AM.Move(get_step(src, dir))
+
+/datum/design/conveyor_sorter
+ name = "Conveyor Sorter"
+ desc = "A wonderful item that can set markers and forcefully move stuff to a direction."
+ id = "conveyor_sorter"
+ build_type = PROTOLATHE
+ build_path = /obj/item/conveyor_sorter
+ materials = list(/datum/material/iron = 500, /datum/material/plastic = 500)
+ category = list("Equipment")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
diff --git a/code/modules/research/techweb/nodes/engineering_nodes.dm b/code/modules/research/techweb/nodes/engineering_nodes.dm
index 1deb97c3bd..51b9480c27 100644
--- a/code/modules/research/techweb/nodes/engineering_nodes.dm
+++ b/code/modules/research/techweb/nodes/engineering_nodes.dm
@@ -7,8 +7,8 @@
prereq_ids = list("base")
design_ids = list("solarcontrol", "recharger", "powermonitor", "rped", "pacman", "adv_capacitor", "adv_scanning", "emitter", "high_cell", "adv_matter_bin",
"atmosalerts", "atmos_control", "recycler", "autolathe", "autolathe_secure", "high_micro_laser", "nano_mani", "mesons", "thermomachine", "rad_collector", "tesla_coil", "grounding_rod",
- "apc_control", "power control", "airlock_board", "firelock_board", "airalarm_electronics", "firealarm_electronics", "cell_charger", "stack_console", "stack_machine", "rcd_ammo","oxygen_tank",
- "plasma_tank", "emergency_oxygen", "emergency_oxygen_engi", "plasmaman_tank_belt", "colormate")
+ "apc_control", "power control", "airlock_board", "firelock_board", "airalarm_electronics", "firealarm_electronics", "cell_charger", "stack_console", "stack_machine", "rcd_ammo", "oxygen_tank",
+ "plasma_tank", "emergency_oxygen", "emergency_oxygen_engi", "plasmaman_tank_belt", "colormate", "conveyor_sorter")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 6000)
/datum/techweb_node/adv_engi
diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm
index 81b2db6cf8..e024718f4c 100644
--- a/code/modules/vehicles/cars/car.dm
+++ b/code/modules/vehicles/cars/car.dm
@@ -2,6 +2,7 @@
layer = ABOVE_MOB_LAYER
anchored = TRUE
default_driver_move = FALSE
+ var/explode_on_death = TRUE
var/car_traits = NONE //Bitflag for special behavior such as kidnapping
var/engine_sound = 'sound/vehicles/carrev.ogg'
var/last_enginesound_time
@@ -12,6 +13,11 @@
. = ..()
LoadComponent(/datum/component/riding)
+/obj/vehicle/sealed/car/Destroy()
+ . = ..()
+ if(explode_on_death)
+ explosion(loc, 0, 1, 2, 3, 0)
+
/obj/vehicle/sealed/car/generate_actions()
. = ..()
initialize_controller_action_type(/datum/action/vehicle/sealed/remove_key, VEHICLE_CONTROL_DRIVE)
diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm
index 1a1858522a..7bb4e878ec 100644
--- a/code/modules/vehicles/sealed.dm
+++ b/code/modules/vehicles/sealed.dm
@@ -1,7 +1,6 @@
/obj/vehicle/sealed
enclosed = TRUE // you're in a sealed vehicle dont get dinked idiot
var/enter_delay = 20
- var/explode_on_death = TRUE
flags_1 = BLOCK_FACE_ATOM_1
/obj/vehicle/sealed/generate_actions()
@@ -90,8 +89,6 @@
/obj/vehicle/sealed/Destroy()
DumpMobs()
- if(explode_on_death)
- explosion(loc, 0, 1, 2, 3, 0)
return ..()
/obj/vehicle/sealed/proc/DumpMobs(randomstep = TRUE)
diff --git a/icons/obj/recycling.dmi b/icons/obj/recycling.dmi
index 9604d6b97e..621b9e4241 100644
Binary files a/icons/obj/recycling.dmi and b/icons/obj/recycling.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 87c95a51b8..43bab0253b 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -689,6 +689,7 @@
#include "code\datums\elements\bed_tucking.dm"
#include "code\datums\elements\bsa_blocker.dm"
#include "code\datums\elements\cleaning.dm"
+#include "code\datums\elements\connect_loc.dm"
#include "code\datums\elements\decal.dm"
#include "code\datums\elements\dusts_on_catatonia.dm"
#include "code\datums\elements\dusts_on_leaving_area.dm"
@@ -3355,6 +3356,7 @@
#include "code\modules\reagents\reagent_containers\spray.dm"
#include "code\modules\reagents\reagent_containers\syringes.dm"
#include "code\modules\recycling\conveyor2.dm"
+#include "code\modules\recycling\conveyor_sorter.dm"
#include "code\modules\recycling\sortingmachinery.dm"
#include "code\modules\recycling\disposal\bin.dm"
#include "code\modules\recycling\disposal\construction.dm"