From 646d4492d543447d740d8c0be6094dbf8a2fc00d Mon Sep 17 00:00:00 2001 From: NovaBot <154629622+NovaBot13@users.noreply.github.com> Date: Sat, 8 Jun 2024 10:21:43 -0400 Subject: [PATCH] [MIRROR] Add circuit component cameras [MDB IGNORE] (#2926) * Add circuit component cameras (#83578) ## About The Pull Request This PR introduces a limited set of camera components that can be used by surveillance security consoles and the PDA/laptop camera app. components There is four camera components, each limited to a specified shell circuit type. Additionally, drone circuit shells can now use the recharge stations too, much like how mobs with BCIs can recharge. ### New Components drone camera * Drone Camera This camera component captures the surrounding area. It has an option to set the camera range (near 5x5/far 14x14). bci camera * BCI Camera This camera component uses the active user's eyes as a camera function. If the user's sights are damaged, the range will be forced to the near setting. If the user is unconscious/dead/blinded or has no eyes, the stream will be cut off. It has an option to set the camera range (near 5x5/far 14x14). polaroid camera * Polaroid Camera Add-On This camera component streams to a camera network. The camera range is hardcoded to the near setting (5x5). airlock camera * Airlock Camera This camera component streams to a camera network. The camera range is hardcoded to the near setting (5x5). ### Features * The cameras can be EMP'd and will be disabled for 90 seconds if successful * When the cameras are active, they will actively drain the cell's power per second (near range uses 3kJ & far range uses 8kJ) * Advance camera console/AIs can use these cameras, however the camera light is disabled (they will be useless in dark areas) ### Screenshots In Action
This is the drone camera viewed on a security camera console
near
This is the same drone, now set to the far range setting
far
## Why It's Good For The Game This promotes emergent gameplay and improves the overall usefulness for drones as they can be 100% used remotely. ## Changelog :cl: add: Added new circuit camera components qol: Circuit drones can now recharge at recharge stations /:cl: --------- Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com> * Add circuit component cameras --------- Co-authored-by: tmyqlfpir <80724828+tmyqlfpir@users.noreply.github.com> Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com> Co-authored-by: NovaBot13 --- code/game/machinery/rechargestation.dm | 2 +- code/modules/photography/camera/camera.dm | 2 +- .../research/designs/wiremod_designs.dm | 5 + code/modules/research/techweb/all_nodes.dm | 1 + .../wiremod/components/atom/remotecam.dm | 438 ++++++++++++++++++ code/modules/wiremod/shell/airlock.dm | 2 +- code/modules/wiremod/shell/drone.dm | 3 +- tgstation.dme | 1 + 8 files changed, 450 insertions(+), 4 deletions(-) create mode 100644 code/modules/wiremod/components/atom/remotecam.dm diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index 2a2f219e618..4e16a1946c7 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -8,7 +8,7 @@ req_access = list(ACCESS_ROBOTICS) state_open = TRUE circuit = /obj/item/circuitboard/machine/cyborgrecharger - occupant_typecache = list(/mob/living/silicon/robot, /mob/living/carbon/human) + occupant_typecache = list(/mob/living/silicon/robot, /mob/living/carbon/human, /mob/living/circuit_drone) processing_flags = NONE var/recharge_speed var/repairs diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index c8159de9b34..00cb86446bb 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -45,7 +45,7 @@ /obj/item/camera/Initialize(mapload) . = ..() - AddComponent(/datum/component/shell, list(new /obj/item/circuit_component/camera), SHELL_CAPACITY_SMALL) + AddComponent(/datum/component/shell, list(new /obj/item/circuit_component/camera, new /obj/item/circuit_component/remotecam/polaroid), SHELL_CAPACITY_SMALL) /obj/item/camera/attack_self(mob/user) if(!disk) diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm index 204ecaa289b..dbcd7416594 100644 --- a/code/modules/research/designs/wiremod_designs.dm +++ b/code/modules/research/designs/wiremod_designs.dm @@ -467,6 +467,11 @@ id = "comp_assoc_list_pick" build_path = /obj/item/circuit_component/list_pick/assoc +/datum/design/component/bci/bci_camera + name = "BCI Camera" + id = "comp_camera_bci" + build_path = /obj/item/circuit_component/remotecam/bci + /datum/design/compact_remote_shell name = "Compact Remote Shell" desc = "A handheld shell with one big button." diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index e678147e8c1..cb1ec6bb1cc 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -894,6 +894,7 @@ "bci_implanter", "bci_shell", "comp_bar_overlay", + "comp_camera_bci", "comp_counter_overlay", "comp_install_detector", "comp_object_overlay", diff --git a/code/modules/wiremod/components/atom/remotecam.dm b/code/modules/wiremod/components/atom/remotecam.dm new file mode 100644 index 00000000000..3d5afa2381c --- /dev/null +++ b/code/modules/wiremod/components/atom/remotecam.dm @@ -0,0 +1,438 @@ +#define REMOTECAM_RANGE_FAR 7 +#define REMOTECAM_RANGE_NEAR 2 +#define REMOTECAM_ENERGY_USAGE_NEAR 0.003 * STANDARD_CELL_CHARGE //Normal components have 0.001 * STANDARD_CELL_CHARGE, this is expensive to livestream footage +#define REMOTECAM_ENERGY_USAGE_FAR 0.008 * STANDARD_CELL_CHARGE //Far range vision should be expensive, crank this up 8 times +#define REMOTECAM_EMP_RESET 90 SECONDS + +/** + * # Remote Camera Component + * + * Attaches a camera for surveillance-on-the-go. + */ +/obj/item/circuit_component/remotecam + display_name = "Camera Abstract Type" + desc = "This is the abstract parent type - do not use this directly!" + category = "Entity" + circuit_flags = CIRCUIT_NO_DUPLICATES + + /// Starts the cameraa + var/datum/port/input/start + /// Stops the program. + var/datum/port/input/stop + /// Camera range flag (near/far) + var/datum/port/input/camera_range + /// The network to use + var/datum/port/input/network + + /// Allow camera range to be set or not + var/camera_range_settable = TRUE + /// Used only for the BCI shell type, as the COMSIG_MOVABLE_MOVED signal need to be assigned to the user mob, not the shell circuit + var/camera_signal_move_override = FALSE + + /// Camera object + var/obj/machinery/camera/shell_camera = null + /// The shell storing the parent circuit + var/atom/movable/shell_parent = null + /// The shell's type (used for prefix naming) + var/camera_prefix = "Camera" + /// Camera random ID + var/c_tag_random = 0 + + /// Used to store the current process state + var/current_camera_state = FALSE + /// Used to store the current cameranet state + var/current_cameranet_state = TRUE + /// Used to store the camera emp state + var/current_camera_emp = FALSE + /// Used to store the camera emp timer id + var/current_camera_emp_timer_id + /// Used to store the last string used for the camera name + var/current_camera_name = "" + /// Used to store the current camera range setting (near/far) + var/current_camera_range = 0 + /// Used to store the last string used for the camera network + var/current_camera_network = "" + +/obj/item/circuit_component/remotecam/get_ui_notices() + . = ..() + if(camera_range_settable) + . += create_ui_notice("Energy Usage For Near (0) Range: [display_energy(REMOTECAM_ENERGY_USAGE_NEAR)] Per [DisplayTimeText(COMP_CLOCK_DELAY)]", "orange", "clock") + . += create_ui_notice("Energy Usage For Far (1) Range: [display_energy(REMOTECAM_ENERGY_USAGE_FAR)] Per [DisplayTimeText(COMP_CLOCK_DELAY)]", "orange", "clock") + else + . += create_ui_notice("Energy Usage While Active: [display_energy(current_camera_range > 0 ? REMOTECAM_ENERGY_USAGE_FAR : REMOTECAM_ENERGY_USAGE_NEAR)] Per [DisplayTimeText(COMP_CLOCK_DELAY)]", "orange", "clock") + +/obj/item/circuit_component/remotecam/populate_ports() + start = add_input_port("Start", PORT_TYPE_SIGNAL) + stop = add_input_port("Stop", PORT_TYPE_SIGNAL) + if(camera_range_settable) + camera_range = add_input_port("Camera Range", PORT_TYPE_NUMBER, default = 0) + network = add_input_port("Network", PORT_TYPE_STRING, default = "ss13") + + if(camera_range_settable) + current_camera_range = camera_range.value + c_tag_random = rand(1, 999) + +/obj/item/circuit_component/remotecam/register_shell(atom/movable/shell) + shell_parent = shell + stop_process() + +/obj/item/circuit_component/remotecam/unregister_shell(atom/movable/shell) + stop_process() + remove_camera() + shell_parent = null + +/obj/item/circuit_component/remotecam/Destroy() + stop_process() + remove_camera() + shell_parent = null + return ..() + +/obj/item/circuit_component/remotecam/input_received(datum/port/input/port) + if(!shell_parent || !shell_camera) + return + update_camera_name_network() + if(COMPONENT_TRIGGERED_BY(start, port)) + start_process() + cameranet_add() + current_camera_state = TRUE + else if(COMPONENT_TRIGGERED_BY(stop, port)) + stop_process() + close_camera() //Instantly turn off the camera + current_camera_state = FALSE + +/** + * Initializes the camera + */ +/obj/item/circuit_component/remotecam/proc/init_camera() + shell_camera.desc = "This camera belongs in a circuit. If you see this, tell a coder!" + shell_camera.AddElement(/datum/element/empprotection, EMP_PROTECT_ALL) + shell_camera.use_power = NO_POWER_USE + shell_camera.start_active = TRUE + shell_camera.internal_light = FALSE + current_camera_name = "" + if(camera_range_settable) + current_camera_range = camera_range.value + current_cameranet_state = TRUE + current_camera_emp = FALSE + current_camera_network = "" + close_camera() + update_camera_range() + update_camera_name_network() + if(current_camera_state) + start_process() + update_camera_location() + else + cameranet_remove() //Remove camera from global cameranet until user activates the camera first + if(!camera_signal_move_override) + RegisterSignal(shell_parent, COMSIG_MOVABLE_MOVED, PROC_REF(update_camera_location)) + RegisterSignal(shell_parent, COMSIG_ATOM_EMP_ACT, PROC_REF(set_camera_emp)) + +/** + * Remove the camera + */ +/obj/item/circuit_component/remotecam/proc/remove_camera() + if(!shell_camera) + return + if(!camera_signal_move_override) + UnregisterSignal(shell_parent, COMSIG_MOVABLE_MOVED) + UnregisterSignal(shell_parent, COMSIG_ATOM_EMP_ACT) + if(current_camera_emp) + deltimer(current_camera_emp_timer_id) + current_camera_emp = FALSE + cameranet_add() //Readd camera to cameranet before deleting camera + QDEL_NULL(shell_camera) + +/** + * Close the camera state (only if it's already active) + */ +/obj/item/circuit_component/remotecam/proc/close_camera() + if(shell_camera?.camera_enabled) + shell_camera.toggle_cam(null, 0) + +/** + * Set the camera range + */ +/obj/item/circuit_component/remotecam/proc/update_camera_range() + shell_camera.setViewRange(current_camera_range > 0 ? REMOTECAM_RANGE_FAR : REMOTECAM_RANGE_NEAR) + +/** + * Updates the camera name and network + */ +/obj/item/circuit_component/remotecam/proc/update_camera_name_network() + if(!parent || !parent.display_name || parent.display_name == "") + shell_camera.c_tag = "[camera_prefix]: unspecified #[c_tag_random]" + current_camera_name = "" + else if(current_camera_name != parent.display_name) + current_camera_name = parent.display_name + var/new_cam_name = reject_bad_name(current_camera_name, allow_numbers = TRUE, ascii_only = FALSE, strict = TRUE, cap_after_symbols = FALSE) + //Set camera name using parent circuit name + if(new_cam_name) + shell_camera.c_tag = "[camera_prefix]: [new_cam_name] #[c_tag_random]" + else + shell_camera.c_tag = "[camera_prefix]: unspecified #[c_tag_random]" + + if(!network.value || network.value == "") + shell_camera.network = list("ss13") + current_camera_network = "" + else if(current_camera_network != network.value) + current_camera_network = network.value + var/new_net_name = LOWER_TEXT(sanitize(current_camera_network)) + //Set camera network string + if(new_net_name) + shell_camera.network = list("[new_net_name]") + else + shell_camera.network = list("ss13") + +/** + * Update the chunk for the camera (if enabled) + */ +/obj/item/circuit_component/remotecam/proc/update_camera_location(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + SIGNAL_HANDLER + if(current_camera_state && current_cameranet_state) + GLOB.cameranet.updatePortableCamera(shell_camera, 0.5 SECONDS) + +/** + * Add camera from global cameranet + */ +/obj/item/circuit_component/remotecam/proc/cameranet_add() + if(current_cameranet_state) + return + GLOB.cameranet.cameras += shell_camera + GLOB.cameranet.addCamera(shell_camera) + current_cameranet_state = TRUE + +/** + * Remove camera from global cameranet + */ +/obj/item/circuit_component/remotecam/proc/cameranet_remove() + if(!current_cameranet_state) + return + GLOB.cameranet.removeCamera(shell_camera) + GLOB.cameranet.cameras -= shell_camera + current_cameranet_state = FALSE + +/** + * Set the camera as emp'd + */ +/obj/item/circuit_component/remotecam/proc/set_camera_emp(datum/source, severity, protection) + SIGNAL_HANDLER + if(current_camera_emp) + return + if(!prob(150 / severity)) + return + current_camera_emp = TRUE + close_camera() + current_camera_emp_timer_id = addtimer(CALLBACK(src, PROC_REF(remove_camera_emp)), REMOTECAM_EMP_RESET, TIMER_STOPPABLE) + for(var/mob/M as anything in GLOB.player_list) + if (M.client?.eye == shell_camera) + M.reset_perspective(null) + to_chat(M, span_warning("The screen bursts into static!")) + +/** + * Restore emp'd camera + */ +/obj/item/circuit_component/remotecam/proc/remove_camera_emp() + current_camera_emp = FALSE + +/** + * Adds the component to the SSclock_component process list + * + * Starts draining cell per second while camera is active + */ +/obj/item/circuit_component/remotecam/proc/start_process() + START_PROCESSING(SSclock_component, src) + +/** + * Removes the component to the SSclock_component process list + * + * Stops draining cell per second + */ +/obj/item/circuit_component/remotecam/proc/stop_process() + STOP_PROCESSING(SSclock_component, src) + +/** + * Handle power usage and camera state updating + * + * This is the generic abstract proc - subtypes with specialized logic should use their own copy of process() + */ +/obj/item/circuit_component/remotecam/process(seconds_per_tick) + if(!shell_parent || !shell_camera) + return PROCESS_KILL + //Camera is currently emp'd + if (current_camera_emp) + close_camera() + return + var/obj/item/stock_parts/cell/cell = parent.get_cell() + //If cell doesn't exist, or we ran out of power + if(!cell?.use(current_camera_range > 0 ? REMOTECAM_ENERGY_USAGE_FAR : REMOTECAM_ENERGY_USAGE_NEAR)) + close_camera() + return + if(camera_range_settable) + //If the camera range has changed, update camera range + if(!camera_range.value != !current_camera_range) + current_camera_range = camera_range.value + update_camera_range() + //Set the camera state (if state has been changed) + if(current_camera_state ^ shell_camera.camera_enabled) + shell_camera.toggle_cam(null, 0) + +/obj/item/circuit_component/remotecam/bci + display_name = "BCI Camera" + desc = "Digitizes user's sight for surveillance-on-the-go. User must have fully functional eyes for digitizer to work. Camera range input is either 0 (near) or 1 (far). Network field is used for camera network." + category = "BCI" + camera_prefix = "BCI" + required_shells = list(/obj/item/organ/internal/cyberimp/bci) + + /// BCIs are organs, and thus the signal must be assigned ONLY when the shell has been installed in a mob - otherwise the camera will never update position + camera_signal_move_override = TRUE + + /// Store the BCI owner as a variable, so we can remove the move signal if the user was gibbed/destroyed while the BCI is still installed + var/mob/living/carbon/bciuser = null + +/obj/item/circuit_component/remotecam/drone + display_name = "Remote Camera" + desc = "Capture the surrounding environment for surveillance-on-the-go. Camera range input is either 0 (near) or 1 (far). Network field is used for camera network." + camera_prefix = "Drone" + +/obj/item/circuit_component/remotecam/airlock + display_name = "Peephole Camera" + desc = "A peephole camera that captures both sides of the airlock. Network field is used for camera network." + camera_prefix = "Airlock" + + /// Hardcode camera to near range + camera_range_settable = FALSE + current_camera_range = 0 + +/obj/item/circuit_component/remotecam/polaroid + display_name = "Camera Stream Add-On" + desc = "Relays a polaroid camera's feed as a digital stream for surveillance-on-the-go. The camera stream will not work if stored inside of a container like a backpack/box. Network field is used for camera network." + camera_prefix = "Polaroid" + + /// Hardcode camera to near range + camera_range_settable = FALSE + current_camera_range = 0 + +/obj/item/circuit_component/remotecam/bci/register_shell(atom/movable/shell) + . = ..() + if(!istype(shell_parent, /obj/item/organ/internal/cyberimp/bci)) + return + shell_camera = new /obj/machinery/camera (shell_parent) + init_camera() + RegisterSignal(shell_parent, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_implanted)) + RegisterSignal(shell_parent, COMSIG_ORGAN_REMOVED, PROC_REF(on_organ_removed)) + var/obj/item/organ/internal/cyberimp/bci/bci = shell_parent + if(bci.owner) //If somehow the camera was added while shell is already installed inside a mob, assign signals + if(bciuser) //This should never happen... But if it does, unassign move signal from old mob + UnregisterSignal(bciuser, COMSIG_MOVABLE_MOVED, PROC_REF(update_camera_location)) + bciuser = bci.owner + RegisterSignal(bciuser, COMSIG_MOVABLE_MOVED, PROC_REF(update_camera_location)) + +/obj/item/circuit_component/remotecam/bci/unregister_shell(atom/movable/shell) + if(shell_camera) + if(bciuser) + UnregisterSignal(bciuser, COMSIG_MOVABLE_MOVED, PROC_REF(update_camera_location)) + bciuser = null + UnregisterSignal(shell_parent, list(COMSIG_ORGAN_IMPLANTED, COMSIG_ORGAN_REMOVED)) + return ..() + +/obj/item/circuit_component/remotecam/bci/Destroy() + if(shell_camera) + if(bciuser) + UnregisterSignal(bciuser, COMSIG_MOVABLE_MOVED, PROC_REF(update_camera_location)) + bciuser = null + UnregisterSignal(shell_parent, list(COMSIG_ORGAN_IMPLANTED, COMSIG_ORGAN_REMOVED)) + return ..() + +/obj/item/circuit_component/remotecam/bci/proc/on_organ_implanted(datum/source, mob/living/carbon/owner) + SIGNAL_HANDLER + if(bciuser) + return + bciuser = owner + RegisterSignal(bciuser, COMSIG_MOVABLE_MOVED, PROC_REF(update_camera_location)) + +/obj/item/circuit_component/remotecam/bci/proc/on_organ_removed(datum/source, mob/living/carbon/owner) + SIGNAL_HANDLER + if(!bciuser) + return + UnregisterSignal(bciuser, COMSIG_MOVABLE_MOVED, PROC_REF(update_camera_location)) + bciuser = null + +/obj/item/circuit_component/remotecam/drone/register_shell(atom/movable/shell) + . = ..() + if(!istype(shell_parent, /mob/living/circuit_drone)) + return + current_camera_state = FALSE //Always reset camera state for built-in shell components + shell_camera = new /obj/machinery/camera (shell_parent) + init_camera() + +/obj/item/circuit_component/remotecam/airlock/register_shell(atom/movable/shell) + . = ..() + if(!istype(shell_parent, /obj/machinery/door/airlock)) + return + current_camera_state = FALSE //Always reset camera state for built-in shell components + shell_camera = new /obj/machinery/camera (shell_parent) + init_camera() + +/obj/item/circuit_component/remotecam/polaroid/register_shell(atom/movable/shell) + . = ..() + if(!istype(shell_parent, /obj/item/camera)) + return + current_camera_state = FALSE //Always reset camera state for built-in shell components + shell_camera = new /obj/machinery/camera (shell_parent) + init_camera() + +/obj/item/circuit_component/remotecam/bci/process(seconds_per_tick) + if(!shell_parent || !shell_camera) + return PROCESS_KILL + //Camera is currently emp'd + if (current_camera_emp) + close_camera() + return + var/obj/item/organ/internal/cyberimp/bci/bci = shell_parent + //If shell is not currently inside a head, or user is currently blind, or user is dead + if(!bci.owner || bci.owner.is_blind() || bci.owner.stat >= UNCONSCIOUS) + close_camera() + return + var/obj/item/stock_parts/cell/cell = parent.get_cell() + //If cell doesn't exist, or we ran out of power + if(!cell?.use(current_camera_range > 0 ? REMOTECAM_ENERGY_USAGE_FAR : REMOTECAM_ENERGY_USAGE_NEAR)) + close_camera() + return + //If owner is nearsighted, set camera range to short (if it wasn't already) + if(bci.owner.is_nearsighted_currently()) + if(current_camera_range) + current_camera_range = 0 + update_camera_range() + //Else if the camera range has changed, update camera range + else if(!camera_range.value != !current_camera_range) + current_camera_range = camera_range.value + update_camera_range() + //Set the camera state (if state has been changed) + if(current_camera_state ^ shell_camera.camera_enabled) + shell_camera.toggle_cam(null, 0) + +/obj/item/circuit_component/remotecam/polaroid/process(seconds_per_tick) + if(!shell_parent || !shell_camera) + return PROCESS_KILL + //Camera is currently emp'd + if (current_camera_emp) + close_camera() + return + //If camera is stored inside of bag or something, turn it off + if(shell_parent.loc.atom_storage) + close_camera() + return + var/obj/item/stock_parts/cell/cell = parent.get_cell() + //If cell doesn't exist, or we ran out of power + if(!cell?.use(REMOTECAM_ENERGY_USAGE_NEAR)) + close_camera() + return + //Set the camera state (if state has been changed) + if(current_camera_state ^ shell_camera.camera_enabled) + shell_camera.toggle_cam(null, 0) + +#undef REMOTECAM_RANGE_FAR +#undef REMOTECAM_RANGE_NEAR +#undef REMOTECAM_ENERGY_USAGE_NEAR +#undef REMOTECAM_ENERGY_USAGE_FAR +#undef REMOTECAM_EMP_RESET diff --git a/code/modules/wiremod/shell/airlock.dm b/code/modules/wiremod/shell/airlock.dm index 6c5cd11df7d..165949529c4 100644 --- a/code/modules/wiremod/shell/airlock.dm +++ b/code/modules/wiremod/shell/airlock.dm @@ -16,7 +16,7 @@ . = ..() AddComponent( \ /datum/component/shell, \ - unremovable_circuit_components = list(new /obj/item/circuit_component/airlock, new /obj/item/circuit_component/airlock_access_event), \ + unremovable_circuit_components = list(new /obj/item/circuit_component/airlock, new /obj/item/circuit_component/airlock_access_event, new /obj/item/circuit_component/remotecam/airlock), \ capacity = SHELL_CAPACITY_LARGE, \ shell_flags = SHELL_FLAG_ALLOW_FAILURE_ACTION|SHELL_FLAG_REQUIRE_ANCHOR \ ) diff --git a/code/modules/wiremod/shell/drone.dm b/code/modules/wiremod/shell/drone.dm index aaeafb6b551..4627a3cf326 100644 --- a/code/modules/wiremod/shell/drone.dm +++ b/code/modules/wiremod/shell/drone.dm @@ -16,7 +16,8 @@ /mob/living/circuit_drone/Initialize(mapload) . = ..() AddComponent(/datum/component/shell, list( - new /obj/item/circuit_component/bot_circuit() + new /obj/item/circuit_component/bot_circuit(), + new /obj/item/circuit_component/remotecam/drone() ), SHELL_CAPACITY_LARGE) /mob/living/circuit_drone/examine(mob/user) diff --git a/tgstation.dme b/tgstation.dme index 62af7ed933a..5211a7b7c78 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -6224,6 +6224,7 @@ #include "code\modules\wiremod\components\atom\matscanner.dm" #include "code\modules\wiremod\components\atom\pinpointer.dm" #include "code\modules\wiremod\components\atom\reagentscanner.dm" +#include "code\modules\wiremod\components\atom\remotecam.dm" #include "code\modules\wiremod\components\atom\self.dm" #include "code\modules\wiremod\components\atom\species.dm" #include "code\modules\wiremod\components\bci\install_detector.dm"