diff --git a/code/__DEFINES/dcs/signals/signals_circuit.dm b/code/__DEFINES/dcs/signals/signals_circuit.dm index 14be995d6ce..bdc4c3e1a9b 100644 --- a/code/__DEFINES/dcs/signals/signals_circuit.dm +++ b/code/__DEFINES/dcs/signals/signals_circuit.dm @@ -92,3 +92,9 @@ #define COMSIG_SHELL_CIRCUIT_ATTACHED "shell_circuit_attached" ///Sent to the shell component when a circuit is removed. #define COMSIG_SHELL_CIRCUIT_REMOVED "shell_circuit_removed" + +/// Called when a usb port registers signals on a circuit or shell (atom/movable/new_physical_object) +#define COMSIG_USB_PORT_REGISTER_PHYSICAL_OBJECT "usb_port_register_physical_object" + +/// Called when a usb port unregisters signals from a circuit or shell (atom/movable/old_physical_object) +#define COMSIG_USB_PORT_UNREGISTER_PHYSICAL_OBJECT "usb_port_unregister_physical_object" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm index 978b02413ca..3106afa7975 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm @@ -276,3 +276,6 @@ ///from /obj/item/crusher_trophy/on_mark_activate(): (trophy, user) #define COMSIG_MOB_TROPHY_ACTIVATED(identifier) "COMSIG_MOB_TROPHY_ACTIVATED[identifier]" + +/// from /mob/eye/camera/remote/assign_user(): (mob/living/new_user, mob/living/old_user) +#define COMSIG_REMOTE_CAMERA_ASSIGN_USER "remote_camera_assign_user" diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index b693999cddb..13805468562 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -594,3 +594,6 @@ /// Sent from /obj/item/kinetic_crusher/proc/fire_kinetic_blast() : (atom/target, mob/living/user, obj/projectile/destabilizer/destabilizer) #define COMSIG_CRUSHER_FIRED_BLAST "crusher_fired_blast" + +/// Sent from /obj/machinert/console/camera_advanced/attack_hand() : (mob/eye/camera/remote/new_camera) +#define COMSIG_ADVANCED_CAMERA_EYE_CREATED "advanced_camera_eye_created" diff --git a/code/datums/components/usb_port.dm b/code/datums/components/usb_port.dm index c996475a239..b5a370d12c6 100644 --- a/code/datums/components/usb_port.dm +++ b/code/datums/components/usb_port.dm @@ -27,13 +27,21 @@ /// Used to prevent range checking during shuttle movement, which moves atoms en-masse. var/defer_range_checks = 0 -/datum/component/usb_port/Initialize(list/circuit_component_types) + /// An extra callback to invoke when registering this component with its parent. Can be a proc name or a callback datum. + var/extra_registration_callback + + /// An extra callback to invoke when unregistering this component from its parent. Can be a proc name or a callback datum. + var/extra_unregistration_callback + +/datum/component/usb_port/Initialize(list/circuit_component_types, extra_registration_callback, extra_unregistration_callback) if (!isatom(parent)) return COMPONENT_INCOMPATIBLE circuit_components = list() src.circuit_component_types = circuit_component_types + src.extra_registration_callback = extra_registration_callback + src.extra_unregistration_callback = extra_unregistration_callback /datum/component/usb_port/proc/set_circuit_components(list/components) var/should_register = FALSE @@ -65,6 +73,13 @@ for(var/obj/item/circuit_component/component as anything in circuit_components) component.register_usb_parent(parent) + if(extra_registration_callback) + if(istype(extra_registration_callback, /datum/callback)) + var/datum/callback/callback = extra_registration_callback + callback.Invoke(src) + else + call(parent, extra_registration_callback)(src) + /datum/component/usb_port/UnregisterFromParent() UnregisterSignal(parent, list( COMSIG_ATOM_USB_CABLE_TRY_ATTACH, @@ -78,6 +93,13 @@ for(var/obj/item/circuit_component/component as anything in circuit_components) component.unregister_usb_parent(parent) + if(extra_unregistration_callback) + if(istype(extra_unregistration_callback, /datum/callback)) + var/datum/callback/callback = extra_unregistration_callback + callback.Invoke(src) + else + call(parent, extra_unregistration_callback)(src) + unregister_circuit_signals() unregister_physical_signals() attached_circuit = null @@ -125,6 +147,7 @@ if (isnull(physical_object)) return + SEND_SIGNAL(src, COMSIG_USB_PORT_UNREGISTER_PHYSICAL_OBJECT, physical_object) UnregisterSignal(physical_object, list( COMSIG_MOVABLE_MOVED, COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, @@ -209,6 +232,7 @@ RegisterSignal(new_physical_object, COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, PROC_REF(before_physical_object_shuttle_move)) RegisterSignal(new_physical_object, COMSIG_ATOM_AFTER_SHUTTLE_MOVE, PROC_REF(after_physical_object_shuttle_move)) RegisterSignal(new_physical_object, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine_shell)) + SEND_SIGNAL(src, COMSIG_USB_PORT_REGISTER_PHYSICAL_OBJECT, new_physical_object) physical_object = new_physical_object // Adds support for loading circuits without shells but with usb cables, or loading circuits with shells because the shells might not load first. diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 88ae57055df..73827f37ae5 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -25,6 +25,8 @@ var/list/actions = list() ///Should we supress any view changes? var/should_supress_view_changes = TRUE + ///Should we add a usb port to this console? + var/add_usb_port = TRUE interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_REQUIRES_SIGHT @@ -51,6 +53,15 @@ //Camera action button to move down a Z level if(move_down_action) actions += new move_down_action(src) + if(add_usb_port) + AddComponent(/datum/component/usb_port, \ + list( + /obj/item/circuit_component/advanced_camera, + /obj/item/circuit_component/advanced_camera_intercept, + ), \ + extra_registration_callback = PROC_REF(register_usb_port), \ + extra_unregistration_callback = PROC_REF(unregister_usb_port) \ + ) /obj/machinery/computer/camera_advanced/Destroy() unset_machine() @@ -162,6 +173,8 @@ to_chat(user, span_warning("\The [src] flashes a bunch of never-ending errors on the display. Something is really wrong.")) return + SEND_SIGNAL(src, COMSIG_ADVANCED_CAMERA_EYE_CREATED, eyeobj) + var/camera_location var/turf/myturf = get_turf(src) var/consider_zlock = (!!length(z_lock)) @@ -273,3 +286,226 @@ new /obj/item/secure_camera_console_pod(get_turf(src)) qdel(src) return ITEM_INTERACT_SUCCESS + +/// Equipment action component support + +/obj/machinery/computer/camera_advanced/proc/register_usb_port(datum/component/usb_port/port) + RegisterSignal(port, COMSIG_USB_PORT_REGISTER_PHYSICAL_OBJECT, PROC_REF(on_port_register_object)) + RegisterSignal(port, COMSIG_USB_PORT_UNREGISTER_PHYSICAL_OBJECT, PROC_REF(on_port_unregister_object)) + if(port.physical_object) + on_port_register_object(port, port.physical_object) + +/obj/machinery/computer/camera_advanced/proc/on_port_register_object(datum/component/usb_port/source, atom/movable/object) + SIGNAL_HANDLER + var/obj/item/integrated_circuit/circuit = source.attached_circuit + if(object == circuit) + return + RegisterSignal(object, COMSIG_CIRCUIT_ACTION_COMPONENT_REGISTERED, PROC_REF(add_circuit_action)) + RegisterSignal(object, COMSIG_CIRCUIT_ACTION_COMPONENT_UNREGISTERED, PROC_REF(remove_circuit_action)) + for(var/obj/item/circuit_component/equipment_action/action_comp in circuit.attached_components) + add_circuit_action(null, action_comp) + +/obj/machinery/computer/camera_advanced/proc/add_circuit_action(datum/_source, obj/item/circuit_component/equipment_action/action_comp) + SIGNAL_HANDLER + var/datum/action/innate/camera_circuit_action/new_action = new(src, action_comp) + LAZYADD(actions, new_action) + if(current_user) + new_action.Grant(current_user) + +/obj/machinery/computer/camera_advanced/proc/remove_circuit_action(datum/_source, obj/item/circuit_component/equipment_action/action_comp) + SIGNAL_HANDLER + var/datum/action/innate/camera_circuit_action/action = action_comp.granted_to[REF(src)] + if(!istype(action)) + return + LAZYREMOVE(actions, action) + qdel(action) + +/obj/machinery/computer/camera_advanced/proc/on_port_unregister_object(datum/component/usb_port/source, atom/movable/object) + SIGNAL_HANDLER + var/obj/item/integrated_circuit/circuit = source.attached_circuit + for(var/obj/item/circuit_component/equipment_action/action_comp in circuit.attached_components) + remove_circuit_action(null, action_comp) + UnregisterSignal(object, list(COMSIG_CIRCUIT_ACTION_COMPONENT_REGISTERED, COMSIG_CIRCUIT_ACTION_COMPONENT_UNREGISTERED)) + +/obj/machinery/computer/camera_advanced/proc/unregister_usb_port(datum/component/usb_port/port) + if(port.physical_object) + on_port_unregister_object(port, port.physical_object) + UnregisterSignal(port, list(COMSIG_USB_PORT_REGISTER_PHYSICAL_OBJECT, COMSIG_USB_PORT_UNREGISTER_PHYSICAL_OBJECT)) + +/datum/action/innate/camera_circuit_action + name = "Action" + button_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "bci_power" + + var/obj/machinery/computer/camera_advanced/console + var/obj/item/circuit_component/equipment_action/action_comp + +/datum/action/innate/camera_circuit_action/New(obj/machinery/computer/camera_advanced/console, obj/item/circuit_component/equipment_action/action_comp) + . = ..() + src.console = console + action_comp.granted_to[REF(console)] = src + src.action_comp = action_comp + +/datum/action/innate/camera_circuit_action/Destroy() + action_comp.granted_to -= REF(console) + action_comp = null + + return ..() + +/datum/action/innate/camera_circuit_action/Activate() + action_comp.user.set_output(owner) + action_comp.signal.set_output(COMPONENT_SIGNAL) + + return ..() + +/// Advanced camera component + +/obj/item/circuit_component/advanced_camera + display_name = "Advanced Camera Console" + desc = "Gets the position being viewed through the console." + + var/datum/port/output/eye_x + var/datum/port/output/eye_y + var/datum/port/output/eye_z + + var/obj/machinery/computer/camera_advanced/attached_console + +/obj/item/circuit_component/advanced_camera/populate_ports() + eye_x = add_output_port("X", PORT_TYPE_NUMBER) + eye_y = add_output_port("Y", PORT_TYPE_NUMBER) + eye_z = add_output_port("Z", PORT_TYPE_NUMBER) + +/obj/item/circuit_component/advanced_camera/register_usb_parent(atom/movable/parent) + . = ..() + if(istype(parent, /obj/machinery/computer/camera_advanced)) + attached_console = parent + if(attached_console.eyeobj) + register_eyeobj(attached_console.eyeobj) + else + RegisterSignal(attached_console, COMSIG_ADVANCED_CAMERA_EYE_CREATED, PROC_REF(on_parent_eye_created)) + +/obj/item/circuit_component/advanced_camera/proc/on_parent_eye_created(datum/_source, mob/eye/camera/remote/eyeobj) + SIGNAL_HANDLER + UnregisterSignal(attached_console, COMSIG_ADVANCED_CAMERA_EYE_CREATED) + register_eyeobj(eyeobj) + +/obj/item/circuit_component/advanced_camera/proc/register_eyeobj(mob/eye/camera/remote/eyeobj) + RegisterSignal(eyeobj, COMSIG_MOVABLE_MOVED, PROC_REF(on_eyeobj_moved)) + +/obj/item/circuit_component/advanced_camera/unregister_usb_parent(atom/movable/parent) + if(istype(parent, /obj/machinery/computer/camera_advanced)) + UnregisterSignal(attached_console, COMSIG_ADVANCED_CAMERA_EYE_CREATED) + if(attached_console.eyeobj) + UnregisterSignal(attached_console.eyeobj, COMSIG_MOVABLE_MOVED) + attached_console = null + return ..() + +/obj/item/circuit_component/advanced_camera/proc/on_eyeobj_moved(atom/movable/source) + SIGNAL_HANDLER + var/turf/eye_turf = get_turf(source) + if(!eye_turf) + return + if(!GLOB.cameranet.checkTurfVis(eye_turf)) + return + eye_x.set_output(source.x) + eye_y.set_output(source.y) + eye_z.set_output(source.z) + +/// Advanced camera target intercept component + +/obj/item/circuit_component/advanced_camera_intercept + display_name = "Advanced Camera Target Intercept" + desc = "Allows the user to target an entity or position with the console." + + var/datum/port/input/enabled + + var/datum/port/output/target_x + var/datum/port/output/target_y + var/datum/port/output/target_z + + var/datum/port/output/target_port + + var/datum/port/output/primary_click + var/datum/port/output/secondary_click + + var/obj/machinery/computer/camera_advanced/attached_console + +/obj/item/circuit_component/advanced_camera_intercept/populate_ports() + . = ..() + enabled = add_input_port("Enabled", PORT_TYPE_NUMBER) + + target_x = add_output_port("X", PORT_TYPE_NUMBER) + target_y = add_output_port("Y", PORT_TYPE_NUMBER) + target_z = add_output_port("Z", PORT_TYPE_NUMBER) + + target_port = add_output_port("Target", PORT_TYPE_ATOM) + + primary_click = add_output_port("Primary", PORT_TYPE_SIGNAL) + secondary_click = add_output_port("Secondary", PORT_TYPE_SIGNAL) + +/obj/item/circuit_component/advanced_camera_intercept/register_usb_parent(atom/movable/parent) + . = ..() + if(istype(parent, /obj/machinery/computer/camera_advanced)) + attached_console = parent + if(attached_console.eyeobj) + register_eyeobj(attached_console.eyeobj) + else + RegisterSignal(attached_console, COMSIG_ADVANCED_CAMERA_EYE_CREATED, PROC_REF(on_parent_eye_created)) + +/obj/item/circuit_component/advanced_camera_intercept/input_received(datum/port/input/port, list/return_values) + if(port != enabled) + return + if(enabled.value) + attached_console.current_user?.click_intercept = src + else + attached_console.current_user?.click_intercept = null + +/obj/item/circuit_component/advanced_camera_intercept/proc/on_parent_eye_created(datum/_source, mob/eye/camera/remote/eyeobj) + SIGNAL_HANDLER + UnregisterSignal(attached_console, COMSIG_ADVANCED_CAMERA_EYE_CREATED) + register_eyeobj(eyeobj) + +/obj/item/circuit_component/advanced_camera_intercept/proc/register_eyeobj(mob/eye/camera/remote/eyeobj) + RegisterSignal(eyeobj, COMSIG_REMOTE_CAMERA_ASSIGN_USER, PROC_REF(on_parent_assign_user)) + if(enabled.value) + attached_console.current_user?.click_intercept = src + +/obj/item/circuit_component/advanced_camera_intercept/unregister_usb_parent(atom/movable/parent) + if(istype(parent, /obj/machinery/computer/camera_advanced)) + attached_console.current_user?.click_intercept = null + if(attached_console.eyeobj) + UnregisterSignal(attached_console.eyeobj, COMSIG_REMOTE_CAMERA_ASSIGN_USER) + UnregisterSignal(attached_console, COMSIG_ADVANCED_CAMERA_EYE_CREATED) + attached_console = null + return ..() + +/obj/item/circuit_component/advanced_camera_intercept/proc/on_parent_assign_user(datum/_source, mob/living/new_user, mob/living/old_user) + SIGNAL_HANDLER + old_user?.click_intercept = null + if(enabled.value) + new_user?.click_intercept = src + +/obj/item/circuit_component/advanced_camera_intercept/proc/InterceptClickOn(mob/user, params, atom/target) + var/list/modifiers = params2list(params) + if(LAZYACCESS(modifiers, SHIFT_CLICK)) + return + var/turf/target_turf = get_turf(target) + if(!target_turf) + return + if(!GLOB.cameranet.checkTurfVis(target_turf)) + return + if(TIMER_COOLDOWN_RUNNING(parent.shell, COOLDOWN_CIRCUIT_TARGET_INTERCEPT)) + return + target_x.set_output(target.x) + target_y.set_output(target.y) + target_z.set_output(target.z) + + target_port.set_output(target) + + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + secondary_click.set_output(COMPONENT_SIGNAL) + else + primary_click.set_output(COMPONENT_SIGNAL) + if(parent.shell) + TIMER_COOLDOWN_START(parent.shell, COOLDOWN_CIRCUIT_TARGET_INTERCEPT, 1 SECONDS) + return TRUE diff --git a/code/game/objects/structures/construction_console/construction_console.dm b/code/game/objects/structures/construction_console/construction_console.dm index 042511cceb8..249860c99b6 100644 --- a/code/game/objects/structures/construction_console/construction_console.dm +++ b/code/game/objects/structures/construction_console/construction_console.dm @@ -17,6 +17,7 @@ icon_screen = "mining" icon_keyboard = "rd_key" light_color = LIGHT_COLOR_PINK + add_usb_port = FALSE ///Area that the eyeobj will be constrained to. If null, eyeobj will be able to build and move anywhere. var/area/allowed_area ///Assoc. list ("structure_name" : count) that keeps track of the number of special structures that can't be built with an RCD, for example, tiny fans or turrets. diff --git a/code/modules/mob/eye/camera/remote.dm b/code/modules/mob/eye/camera/remote.dm index 901e3b16436..08fc09fd37c 100644 --- a/code/modules/mob/eye/camera/remote.dm +++ b/code/modules/mob/eye/camera/remote.dm @@ -51,6 +51,7 @@ /mob/eye/camera/remote/proc/assign_user(mob/living/new_user) var/mob/living/old_user = user_ref?.resolve() + SEND_SIGNAL(src, COMSIG_REMOTE_CAMERA_ASSIGN_USER, new_user, old_user) if(old_user) old_user.remote_control = null old_user.reset_perspective(null) diff --git a/code/modules/shuttle/shuttle_consoles/navigation_computer.dm b/code/modules/shuttle/shuttle_consoles/navigation_computer.dm index 377955ae328..c10b490950f 100644 --- a/code/modules/shuttle/shuttle_consoles/navigation_computer.dm +++ b/code/modules/shuttle/shuttle_consoles/navigation_computer.dm @@ -3,6 +3,7 @@ desc = "Used to designate a precise transit location for a spacecraft." jump_action = null should_supress_view_changes = FALSE + add_usb_port = FALSE // Docking cameras should only interact with their current z-level. move_up_action = null