From 6b59aebeb507c1defe6e246448c7fac5afd65fd4 Mon Sep 17 00:00:00 2001 From: "Ossa88 (SYNAPSE)" Date: Tue, 24 Mar 2026 01:11:17 -0700 Subject: [PATCH 1/2] Commit 1 --- .../xenobiology/code/xenobio_camera.dm | 139 ++++++++++++++++-- 1 file changed, 128 insertions(+), 11 deletions(-) diff --git a/modular_pentest/modules/xenobiology/code/xenobio_camera.dm b/modular_pentest/modules/xenobiology/code/xenobio_camera.dm index bd0ab5dd0bf..d3198821d35 100644 --- a/modular_pentest/modules/xenobiology/code/xenobio_camera.dm +++ b/modular_pentest/modules/xenobiology/code/xenobio_camera.dm @@ -17,7 +17,8 @@ /mob/camera/aiEye/remote/xenobio/setLoc(t) var/area/new_area = get_area(t) - if(new_area && new_area.name == allowed_area || new_area && (new_area.area_flags & XENOBIOLOGY_COMPATIBLE)) + // Only allow movement within the same area name + if(new_area && new_area.name == allowed_area) return ..() else return @@ -25,7 +26,7 @@ /obj/machinery/computer/camera_advanced/xenobio name = "Slime management console" desc = "A computer used for remotely handling slimes." - networks = list("ss13") + networks = list("xenobio_local") // Dummy network that doesn't match anything circuit = /obj/item/circuitboard/computer/xenobiology var/datum/action/innate/slime_place/slime_place_action var/datum/action/innate/slime_pick_up/slime_up_action @@ -41,6 +42,9 @@ var/max_slimes = 5 var/monkeys = 0 + /// Internal camera built into the console as a fallback + var/obj/machinery/camera/internal_camera + icon_screen = "slime_comp" icon_keyboard = "rd_key" @@ -56,6 +60,13 @@ potion_action = new hotkey_help = new stored_slimes = list() + + // Create internal camera + internal_camera = new /obj/machinery/camera(src) + internal_camera.c_tag = "[src] Internal Camera" + internal_camera.network = list("xenobio_internal") + internal_camera.internal_light = FALSE // Don't light up when viewed + for(var/obj/machinery/monkey_recycler/recycler in GLOB.monkey_recyclers) if(get_area(recycler.loc) == get_area(loc)) connected_recycler = recycler @@ -70,13 +81,7 @@ if(connected_recycler) connected_recycler.connected -= src connected_recycler = null - return ..() - -/obj/machinery/computer/camera_advanced/xenobio/handle_atom_del(atom/A) - if(A == current_potion) - current_potion = null - if(A in stored_slimes) - stored_slimes -= A + QDEL_NULL(internal_camera) // Clean up our camera return ..() /obj/machinery/computer/camera_advanced/xenobio/CreateEye() @@ -87,8 +92,21 @@ eyeobj.icon_state = "generic_camera" /obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/user) - ..() - + // Don't call parent - we're replacing the jump action + if(off_action) + off_action.target = user + off_action.Grant(user) + actions += off_action + + // Use our custom xenobio jump action instead of the parent's + if(!jump_action || !istype(jump_action, /datum/action/innate/camera_jump/xenobio)) + jump_action = new /datum/action/innate/camera_jump/xenobio() + if(jump_action) + jump_action.target = user + jump_action.Grant(user) + actions += jump_action + + // Rest of the xenobio-specific actions if(slime_up_action) slime_up_action.target = src slime_up_action.Grant(user) @@ -176,6 +194,27 @@ return ..() +/obj/machinery/computer/camera_advanced/xenobio/attack_hand(mob/user, list/modifiers) + . = ..() + if(.) + return + if(!is_operational) + return + + var/mob/living/L = user + if(!eyeobj) + CreateEye() + + if(!eyeobj.eye_initialized) + // Start at internal camera if it exists, otherwise at console + var/turf/starting_turf = internal_camera ? get_turf(internal_camera) : get_turf(src) + eyeobj.eye_initialized = TRUE + give_eye_control(L) + eyeobj.setLoc(starting_turf, TRUE) + else + give_eye_control(L) + eyeobj.setLoc(eyeobj.loc, TRUE) + /obj/machinery/computer/camera_advanced/xenobio/multitool_act(mob/living/user, obj/item/multitool/I) . = ..() if (istype(I) && istype(I.buffer,/obj/machinery/monkey_recycler)) @@ -184,6 +223,84 @@ connected_recycler.connected += src return TRUE +/obj/machinery/computer/camera_advanced/xenobio/proc/get_area_cameras() + var/area/console_area = get_area(src) + if(!console_area) + // Emergency fallback - just return our internal camera + if(internal_camera) + return list(internal_camera) + return list() + + var/list/area_cameras = list() + + // First, add cameras from the area + for(var/obj/machinery/camera/C in console_area.cameras) + if(C.can_use() && C != internal_camera) // Don't double-add our internal camera + area_cameras += C + + // Always include our internal camera as a fallback option + if(internal_camera && internal_camera.can_use()) + area_cameras += internal_camera + + // If we found no cameras at all, at least return our internal one + if(!area_cameras.len && internal_camera) + return list(internal_camera) + + return area_cameras + +/datum/action/innate/camera_jump/xenobio + // Inherits from the parent camera_jump action + +/datum/action/innate/camera_jump/xenobio/Activate() + if(!target || !isliving(target)) + return + var/mob/living/C = target + var/mob/camera/aiEye/remote/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/origin = remote_eye.origin + + // Get cameras in our area (includes internal camera) + var/list/area_cameras = origin.get_area_cameras() + if(!area_cameras.len) + to_chat(C, span_warning("No cameras detected.")) + return + + camera_sort(area_cameras) + + var/list/T = list() + for(var/obj/machinery/camera/cam in area_cameras) + var/cam_name = cam.c_tag + // Mark the internal camera specially so users know it's the console's own camera + if(cam == origin.internal_camera) + cam_name = "[cam.c_tag] (Console)" + T["[cam_name][cam.can_use() ? null : " (Deactivated)"]"] = cam + + playsound(origin, 'sound/machines/terminal_prompt.ogg', 25, FALSE) + var/camera = input("Choose which camera you want to view", "Cameras") as null|anything in T + var/obj/machinery/camera/final = T[camera] + playsound(src, "terminal_type", 25, FALSE) + if(final) + playsound(origin, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) + remote_eye.setLoc(get_turf(final), TRUE) + C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static) + C.clear_fullscreen("flash", 3) + else + playsound(origin, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE) + +/obj/machinery/computer/camera_advanced/xenobio/examine(mob/user) + . = ..() + var/area/console_area = get_area(src) + var/camera_count = 0 + for(var/obj/machinery/camera/C in console_area.cameras) + if(C.can_use() && C != internal_camera) + camera_count++ + + if(camera_count > 0) + . += span_notice("Detects [camera_count] camera[camera_count == 1 ? "" : "s"] in [console_area.name].") + else + . += span_notice("No external cameras detected. Using internal console camera only.") + + . += span_info("The console has a built-in camera for remote viewing.") + /datum/action/innate/slime_place name = "Place Slimes" icon_icon = 'icons/mob/actions/actions_silicon.dmi' From 1f47d19dee2a24952919bd1ca8470bea1b0b3048 Mon Sep 17 00:00:00 2001 From: "Ossa88 (SYNAPSE)" Date: Tue, 24 Mar 2026 01:23:07 -0700 Subject: [PATCH 2/2] Opps accidently removed the handle_atom_del for the potions/slimes --- modular_pentest/modules/xenobiology/code/xenobio_camera.dm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modular_pentest/modules/xenobiology/code/xenobio_camera.dm b/modular_pentest/modules/xenobiology/code/xenobio_camera.dm index d3198821d35..e2cddf309db 100644 --- a/modular_pentest/modules/xenobiology/code/xenobio_camera.dm +++ b/modular_pentest/modules/xenobiology/code/xenobio_camera.dm @@ -84,6 +84,13 @@ QDEL_NULL(internal_camera) // Clean up our camera return ..() +/obj/machinery/computer/camera_advanced/xenobio/handle_atom_del(atom/A) + if(A == current_potion) + current_potion = null + if(A in stored_slimes) + stored_slimes -= A + return ..() + /obj/machinery/computer/camera_advanced/xenobio/CreateEye() eyeobj = new /mob/camera/aiEye/remote/xenobio(get_turf(src)) eyeobj.origin = src