diff --git a/code/__defines/objects.dm b/code/__defines/objects.dm
new file mode 100644
index 0000000000..879bed2cd1
--- /dev/null
+++ b/code/__defines/objects.dm
@@ -0,0 +1,10 @@
+/*
+ * Defines used for miscellaneous objects.
+ * Currently only the home of the Multiool.
+ */
+
+// Multitool Mode Defines.
+
+#define MULTITOOL_MODE_STANDARD "Standard"
+#define MULTITOOL_MODE_INTCIRCUITS "Modular Wiring"
+#define MULTITOOL_MODE_DOORHACK "Advanced Jacking"
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index 21d855c674..e6385c751f 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -18,20 +18,51 @@
matter = list(DEFAULT_WALL_MATERIAL = 50,"glass" = 20)
+ var/mode_index = 1
+ var/toolmode = MULTITOOL_MODE_STANDARD
+ var/list/modes = list(MULTITOOL_MODE_STANDARD, MULTITOOL_MODE_INTCIRCUITS)
+
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage
var/obj/machinery/clonepod/connecting //same for cryopod linkage
var/obj/machinery/connectable //Used to connect machinery.
+ var/weakref_wiring //Used to store weak references for integrated circuitry. This is now the Omnitool.
toolspeed = 1
-/obj/item/device/multitool/attack_self(mob/user)
- var/clear = alert("Do you want to clear the buffers on the [src]?",, "Yes", "No",)
- if(clear == "Yes")
- buffer = null
- connecting = null
- connectable = null
+/obj/item/device/multitool/attack_self(mob/living/user)
+ var/choice = alert("What do you want to do with \the [src]?","Multitool Menu", "Switch Mode", "Clear Buffers", "Cancel")
+ switch(choice)
+ if("Cancel")
+ to_chat(user,"You lower \the [src].")
+ return
+ if("Clear Buffers")
+ to_chat(user,"You clear \the [src]'s memory.")
+ buffer = null
+ connecting = null
+ connectable = null
+ weakref_wiring = null
+ accepting_refs = 0
+ if(toolmode == MULTITOOL_MODE_INTCIRCUITS)
+ accepting_refs = 1
+ if("Switch Mode")
+ mode_switch(user)
+
+ update_icon()
+
+ return ..()
+
+/obj/item/device/multitool/proc/mode_switch(mob/living/user)
+ if(++mode_index > modes.len) mode_index = 1
+
else
- ..()
+ mode_index++
+
+ toolmode = modes[mode_index]
+ to_chat(user,"\The [src] is now set to [toolmode].")
+
+ accepting_refs = (toolmode == MULTITOOL_MODE_INTCIRCUITS)
+
+ return
/obj/item/device/multitool/cyborg
name = "multitool"
@@ -44,4 +75,4 @@
icon = 'icons/obj/abductor.dmi'
icon_state = "multitool"
toolspeed = 0.1
- origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5)
\ No newline at end of file
+ origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5)
diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
index d236dc9da7..8367e2096a 100644
--- a/code/modules/integrated_electronics/core/assemblies.dm
+++ b/code/modules/integrated_electronics/core/assemblies.dm
@@ -247,7 +247,7 @@
return TRUE
else if(istype(I, /obj/item/integrated_circuit))
- if(!user.unEquip(I))
+ if(!user.unEquip(I) && !istype(user, /mob/living/silicon/robot)) //Robots cannot de-equip items in grippers.
return FALSE
if(add_circuit(I, user))
to_chat(user, "You slide \the [I] inside \the [src].")
@@ -323,6 +323,12 @@
if(choice)
choice.ask_for_input(user)
+/obj/item/device/electronic_assembly/attack_robot(mob/user as mob)
+ if(Adjacent(user))
+ return attack_self(user)
+ else
+ return ..()
+
/obj/item/device/electronic_assembly/emp_act(severity)
..()
for(var/atom/movable/AM in contents)
diff --git a/code/modules/integrated_electronics/core/assemblies/generic.dm b/code/modules/integrated_electronics/core/assemblies/generic.dm
index 8929b69d78..2508f9919d 100644
--- a/code/modules/integrated_electronics/core/assemblies/generic.dm
+++ b/code/modules/integrated_electronics/core/assemblies/generic.dm
@@ -215,6 +215,8 @@
user.visible_message("\The [user] attaches \the [src] to the wall.",
"You attach \the [src] to the wall.",
"You hear clicking.")
+ if(istype(user, /mob/living/silicon/robot)) //Robots cannot unequip/drop items, for Safety Reasons.
+ forceMove(T)
user.drop_item(T)
anchored = TRUE
on_anchored()
diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm
index d612bd7211..5c83c1c399 100644
--- a/code/modules/integrated_electronics/core/integrated_circuit.dm
+++ b/code/modules/integrated_electronics/core/integrated_circuit.dm
@@ -233,7 +233,7 @@ a creative player the means to solve many problems. Circuits are held inside an
else
var/datum/integrated_io/io = pin
- io.ask_for_pin_data(usr) // The pins themselves will determine how to ask for data, and will validate the data.
+ io.ask_for_pin_data(usr, held_item) // The pins themselves will determine how to ask for data, and will validate the data.
/*
if(io.io_type == DATA_CHANNEL)
diff --git a/code/modules/integrated_electronics/core/pins.dm b/code/modules/integrated_electronics/core/pins.dm
index ef6f0a6dbc..ddc0b06f8a 100644
--- a/code/modules/integrated_electronics/core/pins.dm
+++ b/code/modules/integrated_electronics/core/pins.dm
@@ -173,7 +173,7 @@ list[](
return !isnull(data)
// This proc asks for the data to write, then writes it.
-/datum/integrated_io/proc/ask_for_pin_data(mob/user)
+/datum/integrated_io/proc/ask_for_pin_data(mob/user, obj/item/I)
var/new_data = ask_for_data_type(user)
write_data_to_pin(new_data)
diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm
index fb9bfb5095..c2c70985c5 100644
--- a/code/modules/integrated_electronics/core/printer.dm
+++ b/code/modules/integrated_electronics/core/printer.dm
@@ -27,6 +27,12 @@
can_clone = TRUE
debug = TRUE
+/obj/item/device/integrated_circuit_printer/attack_robot(mob/user as mob)
+ if(Adjacent(user))
+ return interact(user)
+ else
+ return ..()
+
/obj/item/device/integrated_circuit_printer/attackby(var/obj/item/O, var/mob/user)
if(istype(O,/obj/item/stack/material))
var/obj/item/stack/material/stack = O
@@ -146,6 +152,8 @@
return
if(!debug)
+ if(!Adjacent(usr))
+ to_chat(usr, "You are too far away from \the [src].")
if(metal - cost < 0)
to_chat(usr, "You need [cost] metal to build that!.")
return 1
diff --git a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
index 461965f254..bde09fdf0e 100644
--- a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
+++ b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
@@ -2,8 +2,15 @@
/datum/integrated_io/ref
name = "ref pin"
-/datum/integrated_io/ref/ask_for_pin_data(mob/user) // This clears the pin.
- write_data_to_pin(null)
+/datum/integrated_io/ref/ask_for_pin_data(mob/user, obj/item/I)
+ if(istype(I, /obj/item/device/multitool))
+ var/obj/item/device/multitool/tool = I
+ write_data_to_pin(tool.weakref_wiring)
+ else if(istype(I, /obj/item/device/integrated_electronics/debugger))
+ var/obj/item/device/integrated_electronics/debugger/tool = I
+ write_data_to_pin(tool.data_to_write)
+ else
+ write_data_to_pin(null)
/datum/integrated_io/ref/write_data_to_pin(var/new_data)
if(isnull(new_data) || isweakref(new_data))
@@ -11,4 +18,4 @@
holder.on_data_written()
/datum/integrated_io/ref/display_pin_type()
- return IC_FORMAT_REF
\ No newline at end of file
+ return IC_FORMAT_REF
diff --git a/code/modules/integrated_electronics/core/tools.dm b/code/modules/integrated_electronics/core/tools.dm
index eb42d92dda..dd270ee326 100644
--- a/code/modules/integrated_electronics/core/tools.dm
+++ b/code/modules/integrated_electronics/core/tools.dm
@@ -170,6 +170,7 @@
/obj/item/device/multitool
+ var/accepting_refs
var/datum/integrated_io/selected_io = null
var/mode = 0
@@ -190,6 +191,10 @@
else
if(buffer || connecting || connectable)
icon_state = "multitool_tracking_fail"
+ else if(accepting_refs)
+ icon_state = "multitool_ref_scan"
+ else if(weakref_wiring)
+ icon_state = "multitool_no_camera"
else
icon_state = "multitool"
@@ -239,7 +244,13 @@
io1.holder.interact(user) // This is to update the UI.
update_icon()
-
+/obj/item/device/multitool/afterattack(atom/target, mob/living/user, proximity)
+ if(accepting_refs && toolmode == MULTITOOL_MODE_INTCIRCUITS && proximity)
+ weakref_wiring = weakref(target)
+ visible_message("[user] slides \a [src]'s over \the [target].")
+ to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \
+ now off.")
+ accepting_refs = 0
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
index 20087b19bb..670087dc51 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
@@ -3,6 +3,8 @@
/obj/item/weapon/gripper
name = "magnetic gripper"
desc = "A simple grasping tool specialized in construction and engineering work."
+ description_info = "Ctrl-Clicking on the gripper will drop whatever it is holding.
\
+ Using an object on the gripper will interact with the item inside it, if it exists, instead."
icon = 'icons/obj/device.dmi'
icon_state = "gripper"
@@ -26,6 +28,23 @@
var/force_holder = null //
+/obj/item/weapon/gripper/examine(mob/user)
+ ..()
+ if(wrapped)
+ to_chat(user, "\The [src] is holding \the [wrapped].")
+ wrapped.examine(user)
+
+/obj/item/weapon/gripper/CtrlClick(mob/user)
+ drop_item()
+ return
+
+/obj/item/weapon/gripper/omni
+ name = "omni gripper"
+ desc = "A strange grasping tool that can hold anything a human can, but still maintains the limitations of application its more limited cousins have."
+ icon_state = "gripper-omni"
+
+ can_hold = list(/obj/item) // Testing and Event gripper.
+
// VEEEEERY limited version for mining borgs. Basically only for swapping cells and upgrading the drills.
/obj/item/weapon/gripper/miner
name = "drill maintenance gripper"
@@ -84,7 +103,28 @@
/obj/item/weapon/disposable_teleporter/slime,
/obj/item/slimepotion,
/obj/item/slime_extract,
- /obj/item/weapon/reagent_containers/food/snacks/monkeycube,
+ /obj/item/weapon/reagent_containers/food/snacks/monkeycube
+
+ )
+
+/obj/item/weapon/gripper/circuit
+ name = "circuit assembly gripper"
+ icon_state = "gripper-circ"
+ desc = "A complex grasping tool used for working with circuitry."
+
+ can_hold = list(
+ /obj/item/weapon/cell/device,
+ /obj/item/device/electronic_assembly,
+ /obj/item/device/assembly/electronic_assembly,
+ /obj/item/clothing/under/circuitry,
+ /obj/item/clothing/gloves/circuitry,
+ /obj/item/clothing/glasses/circuitry,
+ /obj/item/clothing/shoes/circuitry,
+ /obj/item/clothing/head/circuitry,
+ /obj/item/clothing/ears/circuitry,
+ /obj/item/clothing/suit/circuitry,
+ /obj/item/weapon/implant/integrated_circuit,
+ /obj/item/integrated_circuit
)
@@ -178,6 +218,21 @@
return wrapped.attack_self(user)
return ..()
+/obj/item/weapon/gripper/attackby(var/obj/item/O, var/mob/user)
+ if(wrapped) // We're interacting with the item inside. If you can hold a cup with 2 fingers and stick a straw in it, you could do that with a gripper and another robotic arm.
+ wrapped.loc = src.loc
+ var/resolved = wrapped.attackby(O, user)
+ if(QDELETED(wrapped) || wrapped.loc != src.loc) //Juuuust in case.
+ wrapped = null
+ if(!resolved && wrapped && O)
+ O.afterattack(wrapped,user,1)
+ if(QDELETED(wrapped) || wrapped.loc != src.loc) // I don't know of a nicer way to do this.
+ wrapped = null
+ if(wrapped)
+ wrapped.loc = src
+ return resolved
+ return ..()
+
/obj/item/weapon/gripper/verb/drop_item()
set name = "Drop Item"
@@ -221,10 +276,13 @@
if(QDELETED(wrapped) || wrapped.loc != src.loc) //qdel check here so it doesn't duplicate/spawn ghost items
wrapped = null
else
+ wrapped.loc = src.loc //To ensure checks pass.
wrapped.attack(M,user)
M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe.
if(QDELETED(wrapped) || wrapped.loc != src.loc)
wrapped = null
+ if(wrapped) //In the event nothing happened to wrapped, go back into the gripper.
+ wrapped.loc = src
return 1
return 0
@@ -249,7 +307,8 @@
wrapped.afterattack(target,user,1)
//wrapped's force was set to zero. This resets it to the value it had before.
- wrapped.force = force_holder
+ if(wrapped)
+ wrapped.force = force_holder
force_holder = null
//If wrapped was neither deleted nor put into target, put it back into the gripper.
if(wrapped && user && (wrapped.loc == user))
@@ -266,6 +325,10 @@
var/obj/item/I = target
+ if(I.anchored)
+ to_chat(user,"You are unable to lift \the [I] from \the [I.loc].")
+ return
+
//Check if the item is blacklisted.
var/grab = 0
for(var/typepath in can_hold)
diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm
index 2416b339ff..eb288f9046 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm
@@ -417,6 +417,7 @@ var/global/list/robot_modules = list(
src.modules += new /obj/item/device/analyzer(src)
src.modules += new /obj/item/taperoll/engineering(src)
src.modules += new /obj/item/weapon/gripper(src)
+ src.modules += new /obj/item/weapon/gripper/circuit(src)
src.modules += new /obj/item/device/lightreplacer(src)
src.modules += new /obj/item/device/pipe_painter(src)
src.modules += new /obj/item/device/floor_painter(src)
@@ -730,6 +731,7 @@ var/global/list/robot_modules = list(
..()
src.modules += new /obj/item/weapon/portable_destructive_analyzer(src)
src.modules += new /obj/item/weapon/gripper/research(src)
+ src.modules += new /obj/item/weapon/gripper/circuit(src)
src.modules += new /obj/item/weapon/gripper/no_use/organ/robotics(src)
src.modules += new /obj/item/weapon/gripper/no_use/mech(src)
src.modules += new /obj/item/weapon/gripper/no_use/loader(src)
diff --git a/html/changelogs/Mechoid-Grippers.yml b/html/changelogs/Mechoid-Grippers.yml
new file mode 100644
index 0000000000..1b4c6f69b3
--- /dev/null
+++ b/html/changelogs/Mechoid-Grippers.yml
@@ -0,0 +1,13 @@
+
+author: Mechoid
+
+delete-after: True
+
+
+changes:
+ - rscadd: "Research and Engineering borgs now have 'Circuit Grippers', used for constructing and operating integrated circuit machinery."
+ - tweak: "Grippers now allow interaction with their contents, by attacking the gripper itself with an item when one is held. I.E., drawing from a beaker with a syringe."
+ - tweak: "Grippers now show their held item's examine information when examined. Tab information added."
+ - tweak: "Cyborgs can now interact with integrated circuit printers and machines when adjacent to them."
+ - tweak: "Multitools now have a menu to switch modes between standard use and integrated circuit reference scanning. Antag multi-tools untouched for now."
+ - bugfix: "Integrated circuit printer now respects adjacency, if it is not set to debug."
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 1b834792bd..6c697f2777 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/vorestation.dme b/vorestation.dme
index 671199425d..f35684c163 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -54,6 +54,7 @@
#include "code\__defines\mobs.dm"
#include "code\__defines\mobs_vr.dm"
#include "code\__defines\nifsoft.dm"
+#include "code\__defines\objects.dm"
#include "code\__defines\planets.dm"
#include "code\__defines\process_scheduler.dm"
#include "code\__defines\qdel.dm"