diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index 43208497026..d9dad06528e 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -658,6 +658,33 @@ to destroy them and players will be able to make replacements.
/obj/item/stock_parts/manipulator = 2,
/obj/item/reagent_containers/glass/beaker = 2)
+/obj/item/circuitboard/dish_drive
+ board_name = "Dish Drive"
+ build_path = /obj/machinery/dish_drive
+ board_type = "machine"
+ origin_tech = "programming=2"
+ req_components = list(
+ /obj/item/stock_parts/manipulator = 1,
+ /obj/item/stock_parts/matter_bin = 1,
+ /obj/item/stack/sheet/glass = 1)
+ var/suction = TRUE
+ var/transmit = TRUE
+
+/obj/item/circuitboard/dish_drive/examine(mob/user)
+ . = ..()
+ . += "Its suction function is [suction ? "enabled" : "disabled"]. Use it in-hand to switch."
+ . += "Its disposal auto-transmit function is [transmit ? "enabled" : "disabled"]. Alt-click it to switch."
+
+/obj/item/circuitboard/dish_drive/attack_self(mob/living/user)
+ suction = !suction
+ to_chat(user, "You [suction ? "enable" : "disable"] the board's suction function.")
+
+/obj/item/circuitboard/dish_drive/AltClick(mob/living/user)
+ if(!user.Adjacent(src))
+ return
+ transmit = !transmit
+ to_chat(user, "You [transmit ? "enable" : "disable"] the board's automatic disposal transmission.")
+
/obj/item/circuitboard/chem_dispenser/soda
board_name = "Soda Machine"
build_path = /obj/machinery/chem_dispenser/soda
diff --git a/code/game/machinery/dish_drive.dm b/code/game/machinery/dish_drive.dm
new file mode 100644
index 00000000000..ad505a4bf5f
--- /dev/null
+++ b/code/game/machinery/dish_drive.dm
@@ -0,0 +1,134 @@
+/obj/machinery/dish_drive
+ name = "dish drive"
+ desc = "A culinary marvel that uses matter-to-energy conversion to store dishes and shards. Convenient! \
+ Additional features include a vacuum function to suck in nearby dishes, and an automatic transfer beam that empties its contents into nearby disposal bins every now and then. \
+ Or you can just drop your plates on the floor, like civilized folk."
+ icon = 'icons/obj/kitchen.dmi'
+ icon_state = "synthesizer"
+ idle_power_usage = 8 //5 with default parts
+ active_power_usage = 13 //10 with default parts
+ anchored = FALSE
+ density = FALSE
+ pass_flags = PASSTABLE
+ var/static/list/collectable_items = list(/obj/item/trash/waffles,
+ /obj/item/trash/plate,
+ /obj/item/trash/tray,
+ /obj/item/reagent_containers/food/drinks/drinkingglass,
+ /obj/item/kitchen/utensil/fork,
+ /obj/item/shard,
+ /obj/item/broken_bottle)
+ var/static/list/disposable_items = list(/obj/item/trash/waffles,
+ /obj/item/trash/plate,
+ /obj/item/trash/tray,
+ /obj/item/shard,
+ /obj/item/broken_bottle)
+ var/time_since_dishes = 0
+ var/suction_enabled = TRUE
+ var/transmit_enabled = TRUE
+ var/list/dish_drive_contents
+
+/obj/machinery/dish_drive/Initialize(mapload)
+ . = ..()
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/dish_drive(null)
+ component_parts += new /obj/item/stock_parts/manipulator(null)
+ component_parts += new /obj/item/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ RefreshParts()
+
+/obj/machinery/dish_drive/examine(mob/user)
+ . = ..()
+ if(user.Adjacent(src))
+ . += "Alt-click it to beam its waste storage contents to any nearby disposal bins."
+
+/obj/machinery/dish_drive/attack_hand(mob/living/user)
+ if(!LAZYLEN(dish_drive_contents))
+ to_chat(user, "There's nothing in [src]!")
+ return
+ var/obj/item/I = LAZYACCESS(dish_drive_contents, LAZYLEN(dish_drive_contents)) //the most recently-added item
+ LAZYREMOVE(dish_drive_contents, I)
+ user.put_in_hands(I)
+ to_chat(user, "You take out [I] from [src].")
+ playsound(src, 'sound/items/pshoom.ogg', 15, TRUE)
+ flick("synthesizer_beam", src)
+
+/obj/machinery/dish_drive/screwdriver_act(mob/user, obj/item/I)
+ if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I))
+ return TRUE
+
+/obj/machinery/dish_drive/wrench_act(mob/user, obj/item/I)
+ if(default_unfasten_wrench(user, I))
+ return TRUE
+
+/obj/machinery/dish_drive/RefreshParts()
+ idle_power_usage = initial(idle_power_usage)
+ active_power_usage = initial(active_power_usage)
+ use_power = initial(use_power)
+ var/total_rating = 0
+ for(var/obj/item/stock_parts/S in component_parts)
+ total_rating += S.rating
+ if(total_rating >= 9)
+ active_power_usage = 0
+ use_power = NO_POWER_USE
+ else
+ idle_power_usage = max(0, idle_power_usage - total_rating)
+ active_power_usage = max(0, active_power_usage - total_rating)
+ var/obj/item/circuitboard/dish_drive/board = locate() in component_parts
+ if(board)
+ suction_enabled = board.suction
+ transmit_enabled = board.transmit
+
+/obj/machinery/dish_drive/process()
+ if(time_since_dishes <= world.time && transmit_enabled)
+ do_the_dishes()
+ if(!suction_enabled)
+ return
+ for(var/obj/item/I in view(4, src))
+ if(is_type_in_list(I, collectable_items) && I.loc != src && (!I.reagents || !I.reagents.total_volume))
+ if(I.Adjacent(src))
+ LAZYADD(dish_drive_contents, I)
+ visible_message("[src] beams up [I]!")
+ I.forceMove(src)
+ playsound(src, 'sound/items/pshoom.ogg', 15, TRUE)
+ flick("synthesizer_beam", src)
+ else
+ step_towards(I, src)
+
+/obj/machinery/dish_drive/attack_ai(mob/living/user)
+ if(stat)
+ return
+ to_chat(user, "You send a disposal transmission signal to [src].")
+ do_the_dishes(TRUE)
+
+/obj/machinery/dish_drive/AltClick(mob/living/user)
+ if(user.can_use(src, !issilicon(user)))
+ do_the_dishes(TRUE)
+
+/obj/machinery/dish_drive/proc/do_the_dishes(manual)
+ if(!LAZYLEN(dish_drive_contents))
+ if(manual)
+ visible_message("[src] is empty!")
+ return
+ var/obj/machinery/disposal/bin = locate() in view(7, src)
+ if(!bin)
+ if(manual)
+ visible_message("[src] buzzes. There are no disposal bins in range!")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 15, TRUE)
+ return
+ var/disposed = 0
+ for(var/obj/item/I in dish_drive_contents)
+ if(is_type_in_list(I, disposable_items))
+ LAZYREMOVE(dish_drive_contents, I)
+ I.forceMove(bin)
+ use_power(active_power_usage)
+ disposed++
+ if(disposed)
+ visible_message("[src] [pick("whooshes", "bwooms", "fwooms", "pshooms")] and beams [disposed] stored item\s into the nearby [bin.name].")
+ playsound(src, 'sound/items/pshoom.ogg', 15, TRUE)
+ playsound(bin, 'sound/items/pshoom.ogg', 15, TRUE)
+ Beam(bin, icon_state = "rped_upgrade", time = 5)
+ bin.update_icon()
+ flick("synthesizer_beam", src)
+ else
+ visible_message("There are no disposable items in [src]!")
+ time_since_dishes = world.time + 60 SECONDS
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 09e8bb8122a..711b40784e4 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -2219,6 +2219,7 @@
/obj/item/clothing/accessory/waistcoat = 2,
/obj/item/reagent_containers/glass/rag = 3)
contraband = list(/obj/item/toy/figure/crew/chef = 1)
+ premium = list(/obj/item/storage/box/dish_drive = 1)
refill_canister = /obj/item/vending_refill/chefdrobe
/obj/machinery/vending/bardrobe
@@ -2239,6 +2240,7 @@
/obj/item/clothing/accessory/waistcoat = 2,
/obj/item/reagent_containers/glass/rag = 3)
contraband = list(/obj/item/toy/figure/crew/bartender = 1)
+ premium = list(/obj/item/storage/box/dish_drive = 1)
refill_canister = /obj/item/vending_refill/bardrobe
/obj/machinery/vending/hydrodrobe
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index 9efb1862639..8d1267d80be 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -917,6 +917,19 @@
new /obj/item/implantcase/mindshield(src)
new /obj/item/implanter/mindshield(src)
+/obj/item/storage/box/dish_drive
+ name = "DIY Dish Drive Kit"
+ desc = "Contains everything you need to build your own Dish Drive!"
+
+/obj/item/storage/box/dish_drive/populate_contents()
+ new /obj/item/stack/sheet/metal/(src, 5)
+ new /obj/item/stack/cable_coil/five(src)
+ new /obj/item/circuitboard/dish_drive(src)
+ new /obj/item/stack/sheet/glass(src)
+ new /obj/item/stock_parts/manipulator(src)
+ new /obj/item/stock_parts/matter_bin(src)
+ new /obj/item/screwdriver(src)
+
#undef NODESIGN
#undef NANOTRASEN
#undef SYNDI
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 67b9dc11e1e..d32cfcf7a7c 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -975,6 +975,15 @@
visible_message("[user] butchers [src].")
gib()
+/mob/living/proc/can_use(atom/movable/M, be_close = FALSE)
+ if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
+ to_chat(src, "You can't do that right now!")
+ return FALSE
+ if(be_close && !in_range(M, src))
+ to_chat(src, "You are too far away!")
+ return FALSE
+ return TRUE
+
/mob/living/movement_delay(ignorewalk = 0)
. = ..()
if(isturf(loc))
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index 7bb38b242c3..d3d9003780a 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -515,7 +515,6 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe/cable_restrain
/obj/item/stack/cable_coil/New(loc, length = MAXCOIL, paramcolor = null)
..()
- amount = length
if(paramcolor)
color = paramcolor
pixel_x = rand(-2,2)
@@ -835,6 +834,13 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe/cable_restrain
update_appearance(UPDATE_NAME|UPDATE_ICON_STATE)
update_wclass()
+
+/obj/item/stack/cable_coil/five
+
+// Passes '5' to the parent as `new_amount`, so 5 coils are created.
+/obj/item/stack/cable_coil/five/New(loc, new_amount = 5, merge = TRUE, paramcolor = null)
+ ..()
+
/obj/item/stack/cable_coil/yellow
color = COLOR_YELLOW
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index 663490496b6..c261251744e 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -342,6 +342,16 @@
build_path = /obj/item/circuitboard/smartfridge
category = list ("Misc. Machinery")
+/datum/design/dish_drive
+ name = "Machine Design (Dish Drive Board)"
+ desc = "The circuit board for a dish drive."
+ id = "dishdrive"
+ req_tech = list("programming" = 1)
+ build_type = IMPRINTER
+ materials = list(MAT_GLASS = 1000)
+ build_path = /obj/item/circuitboard/dish_drive
+ category = list("Misc. Machinery")
+
/datum/design/monkey_recycler
name = "Machine Design (Monkey Recycler Board)"
desc = "The circuit board for a monkey recycler."
diff --git a/icons/obj/kitchen.dmi b/icons/obj/kitchen.dmi
index 0d13c90cd15..c162b1007d8 100644
Binary files a/icons/obj/kitchen.dmi and b/icons/obj/kitchen.dmi differ
diff --git a/paradise.dme b/paradise.dme
index b2c5bd9e7df..1c3b64e937d 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -672,6 +672,7 @@
#include "code\game\machinery\cryopod.dm"
#include "code\game\machinery\dance_machine.dm"
#include "code\game\machinery\defib_mount.dm"
+#include "code\game\machinery\dish_drive.dm"
#include "code\game\machinery\deployable.dm"
#include "code\game\machinery\door_control.dm"
#include "code\game\machinery\doppler_array.dm"