/** * Multitool -- A multitool is used for hacking electronic devices. * */ /obj/item/device/multitool name = "multitool" desc = "This small, handheld device is made of durable, insulated plastic. It has a electrode jack, perfect for interfacing with numerous machines, as well as an in-built NT-SmartTrack! system." desc_info = "You can use this on airlocks or APCs to try to hack them without cutting wires. You can also use it to wire circuits, and track APCs by using it in-hand." icon = 'icons/obj/contained_items/tools/multitool.dmi' icon_state = "multitool" item_state = "multitool" item_icons = null contained_sprite = TRUE flags = CONDUCT force = 5.0 w_class = ITEMSIZE_SMALL throwforce = 5.0 throw_range = 15 throw_speed = 3 drop_sound = 'sound/items/drop/multitool.ogg' pickup_sound = 'sound/items/pickup/multitool.ogg' matter = list(DEFAULT_WALL_MATERIAL = 50, MATERIAL_GLASS = 20) origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) var/obj/machinery/buffer // simple machine buffer for device linkage var/obj/machinery/clonepod/connecting //same for cryopod linkage var/buffer_name var/atom/buffer_object var/tracking_apc = FALSE var/mutable_appearance/apc_indicator var/datum/integrated_io/selected_io = null var/mode = 0 /obj/item/device/multitool/Destroy() unregister_buffer(buffer_object) QDEL_NULL(apc_indicator) return ..() /obj/item/device/multitool/ismultitool() return TRUE /obj/item/device/multitool/proc/get_buffer(var/typepath) // Only allow clearing the buffer name when someone fetches the buffer. // Means you cannot be sure the source hasn't been destroyed until the very moment it's needed. get_buffer_name(TRUE) if(buffer_object && (!typepath || istype(buffer_object, typepath))) return buffer_object /obj/item/device/multitool/proc/get_buffer_name(var/null_name_if_missing = FALSE) if(buffer_object) buffer_name = buffer_object.name else if(null_name_if_missing) buffer_name = null return buffer_name /obj/item/device/multitool/proc/set_buffer(var/atom/buffer) if(!buffer || istype(buffer)) buffer_name = buffer ? buffer.name : null if(buffer != buffer_object) unregister_buffer(buffer_object) buffer_object = buffer if(buffer_object) destroyed_event.register(buffer_object, src, /obj/item/device/multitool/proc/unregister_buffer) update_icon() /obj/item/device/multitool/proc/unregister_buffer(var/atom/buffer_to_unregister) // Only remove the buffered object, don't reset the name // This means one cannot know if the buffer has been destroyed until one attempts to use it. if(buffer_to_unregister == buffer_object && buffer_object) destroyed_event.unregister(buffer_object, src) buffer_object = null update_icon() /obj/item/device/multitool/resolve_attackby(atom/A, mob/user, var/click_parameters) if(!isobj(A)) return ..(A, user, click_parameters) var/obj/O = A var/datum/component/multitool/MT = O.GetComponent(/datum/component/multitool) if(!MT) return ..(A, user, click_parameters) user.AddTopicPrint(src) MT.interact(src, user) return 1 /obj/item/device/multitool/attack_self(mob/user) interact(user) /obj/item/device/multitool/interact(mob/user) var/datum/vueui/ui = SSvueui.get_open_ui(user, src) if(!ui) ui = new(user, src, "devices-multitool", 300, 250, capitalize_first_letters(name), state = inventory_state) ui.open() /obj/item/device/multitool/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) if(!data) data = list() VUEUI_SET_CHECK(data["tracking_apc"], tracking_apc, ., data) VUEUI_SET_CHECK(data["has_selected_io"], !isnull(selected_io), ., data) if(selected_io) VUEUI_SET_CHECK(data["selected_io_name"], selected_io.name, ., data) VUEUI_SET_CHECK(data["selected_io_type"], selected_io.io_type, ., data) /obj/item/device/multitool/Topic(href, href_list) . = ..() if(.) return TRUE if(href_list["track_apc"]) tracking_apc = !tracking_apc if(tracking_apc) START_PROCESSING(SSprocessing, src) apc_indicator = mutable_appearance(icon, "lost") add_overlay(apc_indicator) else STOP_PROCESSING(SSprocessing, src) QDEL_NULL(apc_indicator) if(href_list["clear_io"]) selected_io = null update_icon() SSvueui.check_uis_for_change(src) /obj/item/device/multitool/update_icon() if(tracking_apc) icon_state = "multitool_clear" else if(selected_io) if(buffer || connecting || buffer_object) icon_state = "multitool_tracking" else icon_state = "multitool_red" else if(buffer || connecting || buffer_object) icon_state = "multitool_tracking_fail" else icon_state = "multitool" /obj/item/device/multitool/process() if(!apc_indicator) return PROCESS_KILL var/turf/T = get_turf(src) var/area/A = T.loc var/obj/machinery/power/apc/APC = A.apc if(!APC || (APC.z != T.z)) apc_indicator.icon_state = "lost" else // shamelessly stolen from technomancer dir = get_dir(T, APC.loc) // overlays can't have different dirs than their atoms, but the multitool only has one dir sprite, so this works switch(get_dist(T, APC.loc)) if(-1 to 0) apc_indicator.icon_state = "direct" if(1 to 8) apc_indicator.icon_state = "close" if(9 to 16) apc_indicator.icon_state = "medium" if(16 to INFINITY) apc_indicator.icon_state = "far" set_overlays(apc_indicator) /obj/item/device/multitool/proc/wire(datum/integrated_io/io, mob/user) if(!io.holder.assembly) to_chat(user, "\The [io.holder] needs to be secured inside an assembly first.") return if(selected_io) if(io == selected_io) to_chat(user, "Wiring \the [selected_io.holder]'s '[selected_io.name]' pin into itself is rather pointless.") return if(io.io_type != selected_io.io_type) to_chat(user, "Those two types of channels are incompatable. The first is a [selected_io.io_type], \ while the second is a [io.io_type].") return if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly) to_chat(user, "Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.") return selected_io.linked |= io io.linked |= selected_io to_chat(user, "You connect \the [selected_io.holder]'s '[selected_io.name]' pin to \the [io.holder]'s '[io.name]' pin.") selected_io.holder.interact(user) // This is to update the UI. selected_io = null else selected_io = io to_chat(user, "You link \the multitool to \the [selected_io.holder]'s [selected_io.name] data channel.") update_icon() SSvueui.check_uis_for_change(src) /obj/item/device/multitool/proc/unwire(datum/integrated_io/io1, datum/integrated_io/io2, mob/user) if(!io1.linked.len || !io2.linked.len) to_chat(user, "There is nothing connected to the data channel.") return if(!(io1 in io2.linked) || !(io2 in io1.linked) ) to_chat(user, "These data pins aren't connected!") return else io1.linked.Remove(io2) io2.linked.Remove(io1) to_chat(user, "You clip the data connection between the [io1.holder.displayed_name]'s \ '[io1.name]' pin and the [io2.holder.displayed_name]'s '[io2.name]' pin.") io1.holder.interact(user) // This is to update the UI. update_icon()