diff --git a/code/modules/buildmode/submodes/forcemove.dm b/code/modules/buildmode/submodes/forcemove.dm
new file mode 100644
index 00000000000..0f2364bdfff
--- /dev/null
+++ b/code/modules/buildmode/submodes/forcemove.dm
@@ -0,0 +1,78 @@
+// This tool calls forceMove() on an object/mob that is selected with right-click.
+// Once the atom is moved, its selection is cleared to avoid any further moves by accident.
+/datum/buildmode_mode/forcemove
+ key = "forcemove"
+ var/atom/movable/selected_atom // The atom we want to move
+ var/image/selected_overlay // Overlay for the selected atom only visible for the build mode user
+
+/datum/buildmode_mode/forcemove/show_help(mob/user)
+ to_chat(user, "***********************************************************")
+ to_chat(user, "Left Mouse Button on obj/mob = Select destination")
+ to_chat(user, "Right Mouse Button on obj/mob = Select atom to move")
+ to_chat(user, "Notice: You need to select the movable atom first, then left-click its destination.")
+ to_chat(user, "***********************************************************")
+
+/datum/buildmode_mode/forcemove/handle_click(mob/user, params, atom/A)
+ var/list/pa = params2list(params)
+ var/left_click = pa.Find("left")
+ var/right_click = pa.Find("right")
+
+ // Selecting the atom to move
+ if(right_click)
+ if(!ismovable(A) || iseffect(A))
+ to_chat(user, "You cannot move effects, turfs or areas.")
+ return
+
+ // If we had something previously selected, handle its signal and overlay
+ if(selected_atom)
+ UnregisterSignal(selected_atom, COMSIG_PARENT_QDELETING)
+
+ if(selected_overlay)
+ remove_selected_overlay(user)
+
+ // We ensure it properly gets GC'd
+ selected_atom = A
+ RegisterSignal(selected_atom, COMSIG_PARENT_QDELETING, PROC_REF(on_selected_atom_deleted))
+
+ // Green overlay for selection
+ selected_overlay = image(icon = selected_atom.icon, loc = A, icon_state = selected_atom.icon_state)
+ selected_overlay.color = "#15d12d"
+ user.client.images += selected_overlay
+
+ to_chat(user, "'[selected_atom]' is selected to be moved.")
+ return
+
+ // Selecting the destination to move to
+ if(!left_click)
+ return
+
+ var/atom/destination = A
+
+ if(!selected_atom)
+ to_chat(user, "Select an atom to move first (with right-click).")
+ return
+
+ // Block these as they can only lead to issues
+ if(iseffect(destination) || isobserver(destination))
+ to_chat(user, "You should not move atoms into effects or ghosts.")
+ return
+
+ selected_atom.forceMove(destination)
+
+ to_chat(user, "'[selected_atom]' is moved to '[destination]'.")
+ log_admin("Build Mode: [key_name(user)] forcemoved [selected_atom] to [destination] at ([destination.x],[destination.y],[destination.z]).")
+
+ UnregisterSignal(selected_atom, COMSIG_PARENT_QDELETING)
+ selected_atom = null
+ remove_selected_overlay(user)
+
+// Remove the green selection (only visible for the user)
+/datum/buildmode_mode/forcemove/proc/remove_selected_overlay(mob/user)
+ user.client.images -= selected_overlay
+ selected_overlay = null
+
+// If it gets deleted mid-movement, remove the overlay and its attachment to the forcemove tool
+/datum/buildmode_mode/forcemove/proc/on_selected_atom_deleted()
+ SIGNAL_HANDLER
+ UnregisterSignal(selected_atom, COMSIG_PARENT_QDELETING)
+ selected_atom = null
diff --git a/icons/misc/buildmode.dmi b/icons/misc/buildmode.dmi
index f5eaff893b3..149a13d7b71 100644
Binary files a/icons/misc/buildmode.dmi and b/icons/misc/buildmode.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 06e60026f96..c1f4c4223f5 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1444,6 +1444,7 @@
#include "code\modules\buildmode\submodes\boom.dm"
#include "code\modules\buildmode\submodes\copy.dm"
#include "code\modules\buildmode\submodes\fill.dm"
+#include "code\modules\buildmode\submodes\forcemove.dm"
#include "code\modules\buildmode\submodes\link.dm"
#include "code\modules\buildmode\submodes\mapgen.dm"
#include "code\modules\buildmode\submodes\save.dm"