From 428ece899fc80b485cfc3036390b0546a4c03114 Mon Sep 17 00:00:00 2001
From: Adri <33333517+Miraviel@users.noreply.github.com>
Date: Fri, 13 Jan 2023 16:24:43 +0100
Subject: [PATCH] Admin tools: Adds a forceMove tool to build mode (#20073)
* Admin tools: Adds a forceMove tool to build mode
* Updated the misleading explanation
* Adds early return, removes dangling return
* Ryan review + observer disallow
* Replaces copypaste
* Adds selection overlay, signals
* Removes unnecessary typecasting
* this will never work anyhow
* Adds signal_handler
Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>
Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>
---
code/modules/buildmode/submodes/forcemove.dm | 78 +++++++++++++++++++
icons/misc/buildmode.dmi | Bin 4065 -> 1781 bytes
paradise.dme | 1 +
3 files changed, 79 insertions(+)
create mode 100644 code/modules/buildmode/submodes/forcemove.dm
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 f5eaff893b30bd66c8c5917ec9e1dc429a4d29bc..149a13d7b71ec048c397b42a407d0fcbb59c51ca 100644
GIT binary patch
delta 1777
zcmV3FZsUApJe)CUM^P~
zu~@Ho3qBw@F%y%)kh3$YsJk#8ai#(HIzB>(GD27OsuLR3<4mCsU7m8b^U}htF5V~&
zrg@UZ3&{*o^TDyCU|Y$6b(SerD_spLO9q88ON`pnIm@)N*@OG0z6!@v=6E$Vm;YKk
zZsiTNhkwQ-;0E0x0k4Q`0v==wDeFc{A!S$cHCX!Iqc6(M;{ID#Yg+^a+^Gxv
z000G@Nklx0T3iX{UJs3
zrR|L6m&9Kz%Rhr4@ez8<4?n5n$5-|8_^giSt9p4n%YP4eJYUtzGft?Sn-Z2>&%&1T316O3187~pt~I!IH9$HLZpDG0xaChwx_(`2%fh2R
zBQ)*?A)mXN3&I$~K0+(!`$>dOl$k?l^Z9-Xp~=$CBLtX7h-U)*tmY=6W$pF(J|-gm
z+fQf!4}V877=!0K=L*7_Xs78afSRA>*S)nC&-dOJgsqsh2yuun2@gm0G=>|{
z&vt{bm501KxtoL+dB}&KN67D=LzrV~w<(0U4)XimI)_jYZLwmTm{>!EYg}(!T(PN&
z@oLds
z*MHUmC^6jSq4sxeT>u_b!KLHP(UoplM@Hq(pNEj`@`?<
z5C7Ks*S9}B?H>;7v#!VuZL;@?-bVSmfgL@FitC~FcLT&A{xNx*(mf6Amjw}|rYL_m
z5>rM**EmQRd=>y1vgj8f0!r0$H)H5re8+K@So=GQNq81&82C7ffj^uRyiNQD;eUxD
zBt+WC9Ooginp{JWCLXdYAj0As1m6JbnyOwsY8XwM_a)v2T*OL*9Q7i`x$n(Yj0g
zF)_daIwd@aaq7%-UA7?tHr3rdAw>-xjEpMU1Y2f7+F9{V3hjw;a1z5E5gS6i_sd<8
zPsl?t@tYdua&M>eFuVcq9mU?yi+|^i-o`oJz4dmI9J#G>?CTT
zOG0rL1tH+)4z4ylN#Zv=>to;2oP!pw?x)<*3`mG`2toWK%pnv&MEo+N>iwBL_+N8k
zsPx+(POx+J39sr8@Q1&ZA22W|)%*W1{t$07O`9pw%gTKi^bwx^8d!ab{(oU|0?ts;
zFkSoOBf{8JNHqcru$L}J*ZmZ0(|x|Oe<)QCz<@)y(n}~#M+mty1dyEqBHkc`O%cYv
z3=MRSAwYi&D?)0q#`~RPRgZ$M3Hb)RPe+-imI)41^6;9_Z?QjkIm(#5
zR@~Q)5YRN|nWG6E-mD<1w3A}O^jid86V?PZA!)$rR1u~-;6@YHF(M+G;6+Tqn3M%T
zy9k#Ft2}Jw9TTGwKU)v0JZ$A1k2)B0x?-n9?DT__m-cuy%H28o-G7BCbNOpRoZ1!P
zy7%^nBW7v3oxA;p)^YnQH7|J6AGR-e*Z(yycy}f=FL-w*vdyl7tX>u~u^jvx5-)o{*XzHWQ}KQxBo52;~@#
zp{Iy}KO1Fw-QE~w;A+m>#60xZgi7}iuCocjbLPz>{N8gJjSO{)N9Xcx<`@^Zf3paA
zD)Zc)LFm|?I_RCt9Wnd}LYNps8enSv|2uj3pRam%{a^Efcl}@Ug0s!iY&-W4Tng>8
T$Ge&G00000NkvXXu0mjfF|A`2
literal 4065
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
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"