From 2b0da7a1ce4abf51f35e7d5647b0d56c8554ef98 Mon Sep 17 00:00:00 2001 From: Roxy <75404941+TealSeer@users.noreply.github.com> Date: Wed, 10 Jun 2026 21:28:05 -0400 Subject: [PATCH] Security camera `attackby` to `item_interaction` (#96361) ## About The Pull Request Rewrite the big `attackby` for security cameras to use `item_interaction` and distinct `_act` procs, also turns the big clusterfuck for pressing notes up to cameras into a slightly less clusterfuck proc ## Why It's Good For The Game Clean code ## Changelog :cl: refactor: security camera construction/item interaction has been rewritten, report any weirdness /:cl: --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> --- .../machinery/camera/camera_construction.dm | 324 +++++++++--------- 1 file changed, 159 insertions(+), 165 deletions(-) diff --git a/code/game/machinery/camera/camera_construction.dm b/code/game/machinery/camera/camera_construction.dm index 3b3fec8cd5d..b067d19ae0e 100644 --- a/code/game/machinery/camera/camera_construction.dm +++ b/code/game/machinery/camera/camera_construction.dm @@ -71,177 +71,171 @@ return ..() /obj/machinery/camera/wrench_act(mob/user, obj/item/tool) - if(camera_construction_state == CAMERA_STATE_WRENCHED) - tool.play_tool_sound(src) - to_chat(user, span_notice("You detach [src] from its place.")) - deconstruct(TRUE) - return ITEM_INTERACT_SUCCESS - return ..() + if(camera_construction_state != CAMERA_STATE_WRENCHED) + return NONE + tool.play_tool_sound(src) + to_chat(user, span_notice("You detach [src] from its place.")) + deconstruct(TRUE) + return ITEM_INTERACT_SUCCESS /obj/machinery/camera/crowbar_act(mob/living/user, obj/item/tool) - if(camera_construction_state == CAMERA_STATE_FINISHED) - if(!panel_open) - return ITEM_INTERACT_BLOCKING - var/list/droppable_parts = list() - if(xray_module) - droppable_parts += xray_module - if(emp_module) - droppable_parts += emp_module - if(proximity_monitor) - droppable_parts += proximity_monitor - if(!length(droppable_parts)) - return ITEM_INTERACT_BLOCKING - var/obj/item/choice = tgui_input_list(user, "Select a part to remove", "Part Removal", sort_names(droppable_parts)) - if(isnull(choice)) - return ITEM_INTERACT_BLOCKING - if(!user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return ITEM_INTERACT_BLOCKING - to_chat(user, span_notice("You remove [choice] from [src].")) - if(choice == xray_module) - drop_upgrade(xray_module) - removeXRay() - if(choice == emp_module) - drop_upgrade(emp_module) - removeEmpProof() - if(choice == proximity_monitor) - drop_upgrade(proximity_monitor) - removeMotion() - tool.play_tool_sound(src) - return ITEM_INTERACT_SUCCESS - return ..() + if(camera_construction_state != CAMERA_STATE_FINISHED || !panel_open) + return NONE + var/list/droppable_parts = list() + if(xray_module) + droppable_parts += xray_module + if(emp_module) + droppable_parts += emp_module + if(proximity_monitor) + droppable_parts += proximity_monitor + if(!length(droppable_parts)) + return ITEM_INTERACT_BLOCKING + var/obj/item/choice = tgui_input_list(user, "Select a part to remove", "Part Removal", sort_names(droppable_parts)) + if(isnull(choice)) + return ITEM_INTERACT_BLOCKING + if(!user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("You remove [choice] from [src].")) + if(choice == xray_module) + drop_upgrade(xray_module) + removeXRay() + if(choice == emp_module) + drop_upgrade(emp_module) + removeEmpProof() + if(choice == proximity_monitor) + drop_upgrade(proximity_monitor) + removeMotion() + tool.play_tool_sound(src) + return ITEM_INTERACT_SUCCESS /obj/machinery/camera/multitool_act(mob/living/user, obj/item/tool) - if(camera_construction_state == CAMERA_STATE_FINISHED) - if(!panel_open) - return ITEM_INTERACT_BLOCKING - setViewRange((view_range == initial(view_range)) ? short_range : initial(view_range)) - to_chat(user, span_notice("You [(view_range == initial(view_range)) ? "restore" : "mess up"] the camera's focus.")) - return ITEM_INTERACT_SUCCESS - return ..() + if(camera_construction_state != CAMERA_STATE_FINISHED || !panel_open) + return NONE + setViewRange((view_range == initial(view_range)) ? short_range : initial(view_range)) + to_chat(user, span_notice("You [(view_range == initial(view_range)) ? "restore" : "mess up"] the camera's focus.")) + return ITEM_INTERACT_SUCCESS -/obj/machinery/camera/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers) - if(camera_construction_state != CAMERA_STATE_FINISHED || panel_open) - if(attacking_item.tool_behaviour == TOOL_ANALYZER) - if(!isXRay(TRUE)) //don't reveal it was already upgraded if was done via MALF AI Upgrade Camera Network ability - if(!user.temporarilyRemoveItemFromInventory(attacking_item, newloc = src)) - return - upgradeXRay(FALSE, TRUE) - to_chat(user, span_notice("You attach [attacking_item] into [name]'s inner circuits.")) - qdel(attacking_item) +/obj/machinery/camera/proc/gas_analyzer_act(mob/living/user, obj/item/tool) + if(camera_construction_state == CAMERA_STATE_FINISHED && !panel_open) + return NONE + if(isXRay(TRUE)) + to_chat(user, span_warning("[src] already has that upgrade!")) + return ITEM_INTERACT_BLOCKING + if(!user.temporarilyRemoveItemFromInventory(tool, newloc = src)) + return ITEM_INTERACT_BLOCKING + upgradeXRay(FALSE, TRUE) + to_chat(user, span_notice("You attach [tool] into [src]'s inner circuits.")) + qdel(tool) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/camera/proc/plasma_act(mob/living/user, obj/item/tool) + if(camera_construction_state == CAMERA_STATE_FINISHED && !panel_open) + return NONE + if(isEmpProof(TRUE)) + to_chat(user, span_warning("[src] already has that upgrade!")) + return ITEM_INTERACT_BLOCKING + if(!tool.use_tool(src, user, 0, amount = 1)) + return ITEM_INTERACT_BLOCKING + upgradeEmpProof(FALSE, TRUE) + to_chat(user, span_notice("You attach [tool] into [src]'s inner circuits.")) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/camera/proc/prox_act(mob/living/user, obj/item/tool) + if(camera_construction_state == CAMERA_STATE_FINISHED && !panel_open) + return NONE + if(isMotion()) + to_chat(user, span_warning("[src] already has that upgrade!")) + return ITEM_INTERACT_BLOCKING + if(!user.temporarilyRemoveItemFromInventory(tool, newloc = src)) + return ITEM_INTERACT_BLOCKING + upgradeMotion() + to_chat(user, span_notice("You attach [tool] into [src]'s inner circuits.")) + qdel(tool) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/camera/proc/cable_act(mob/living/user, obj/item/tool) + if(camera_construction_state != CAMERA_STATE_WELDED) + return NONE + if(!astype(tool, /obj/item/stack/cable_coil)?.use(2)) + to_chat(user, span_warning("You need two lengths of cable to wire [src]!")) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("You add wires to [src].")) + camera_construction_state = CAMERA_STATE_WIRED + return ITEM_INTERACT_SUCCESS + +/obj/machinery/camera/proc/computer_act(mob/living/user, obj/item/tool) + if(camera_construction_state != CAMERA_STATE_FINISHED) + return NONE + var/obj/item/modular_computer/computer = tool + var/note_name = sanitize(computer.name) + var/datum/computer_file/program/notepad/notepad_app = locate() in computer.stored_files + if(!notepad_app) + return ITEM_INTERACT_BLOCKING + var/note_text = sanitize(notepad_app.written_note) + if(!note_text) + return ITEM_INTERACT_BLOCKING + display_note(user, note_name, note_text, TRUE) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/camera/proc/paper_act(mob/living/user, obj/item/tool) + if(camera_construction_state != CAMERA_STATE_FINISHED) + return NONE + var/obj/item/paper/paper = tool + last_shown_paper = paper.copy(paper.type, null) + var/note_name = sanitize(last_shown_paper.name) + last_shown_paper.camera_holder = WEAKREF(src) + display_note(user, note_name, null, FALSE) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/camera/proc/display_note(mob/living/user, title, text, is_computer) + to_chat(user, span_notice("You hold up \the [title] up to the camera...")) + user.log_talk(title, LOG_GAME, "Pressed to camera", TRUE) + user.changeNext_move(CLICK_CD_MELEE) + + // Iterate over all living mobs and check if anyone is elibile to view the paper. + // This is backwards, but cameras don't store a list of people that are looking through them, + // and we'll have to iterate this list anyway so we can use it to pull out AIs too. + for(var/mob/potential_viewer as anything in GLOB.player_list) + // All AIs view through cameras, so we need to check them regardless. + if(isAI(potential_viewer)) + var/mob/living/silicon/ai/ai = potential_viewer + if(ai.control_disabled || ai.stat == DEAD) + continue + + ai.log_talk(title, LOG_VICTIM, "Pressed to camera from [key_name(user)]", FALSE) + if(is_computer) + ai.last_tablet_note_seen = "