From d8450b493351733fb47af8fbee3e6765f64bbeb2 Mon Sep 17 00:00:00 2001
From: tonty <39193182+tontyGH@users.noreply.github.com>
Date: Thu, 21 Nov 2024 06:55:38 -0500
Subject: [PATCH] Camera eyes have been lightly refactored (among other
things...) (#87805)
## About The Pull Request
* A generic /mob/eye/camera type has been made, containing everything
needed to interface with a cameranet
* /mob/eye/ai_eye has been refactored into a generic /mob/eye/camera
instance
* Advanced cameras no longer inherit from AI eyes, splitting off
behaviour
* Camera code has been somewhat cleaned up
* Probably some more stuff I'm forgetting right now
## Big man Southport:

## Changelog
:cl:
code: made /proc/getviewsize() pure
refactor: mob/eye/ai_eye has been restructured, now inheriting from a
generic mob/eye/camera type
refactor: advanced cameras and their subtypes are now
mob/eye/camera/remote subtypes
code: the cameranet no longer expects the user to be an AI eye
code: remote camera eyes have had their initialization streamlined
code: remote cameras handle assigning and unassigning users by
themselves now
code: remote cameras now use weakrefs instead of hard referencing owners
and origins
code: also the sentient disease is_define was removed (we don't have
those anymore)
fix: AI eyes no longer assign real names to themselves, fixing their
orbit name
/:cl:
---------
Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
---
.github/guides/ISSUE_MANAGER.md | 4 +-
code/__DEFINES/is_helpers.dm | 22 ++-
code/__HELPERS/view.dm | 6 +-
code/_globalvars/lists/mobs.dm | 2 +-
.../plane_masters/plane_master_subtypes.dm | 2 +-
code/datums/components/supermatter_crystal.dm | 2 +-
code/datums/holocall.dm | 26 +--
code/game/atom/_atom.dm | 2 +-
code/game/data_huds.dm | 2 +-
.../machinery/computer/camera_advanced.dm | 176 ++++++------------
code/game/objects/items/devices/multitool.dm | 4 +-
.../construction_actions.dm | 2 +-
.../construction_console.dm | 13 +-
.../antagonists/abductor/machinery/camera.dm | 13 +-
code/modules/forensics/_forensics.dm | 4 +-
code/modules/mob/eye/camera/camera.dm | 80 ++++++++
code/modules/mob/eye/camera/remote.dm | 130 +++++++++++++
code/modules/mob/eye/eye.dm | 6 +-
code/modules/mob/living/silicon/ai/ai.dm | 6 +-
.../living/silicon/ai/freelook/cameranet.dm | 13 +-
.../mob/living/silicon/ai/freelook/chunk.dm | 31 +--
.../mob/living/silicon/ai/freelook/eye.dm | 146 ++++++---------
.../modules/mob/living/silicon/ai/multicam.dm | 33 ++--
.../research/xenobiology/xenobio_camera.dm | 58 +++---
code/modules/shuttle/navigation_computer.dm | 43 ++---
code/modules/unit_tests/mob_faction.dm | 4 +-
code/modules/unit_tests/unit_test.dm | 4 +-
.../vehicles/mecha/mecha_ai_interaction.dm | 2 +-
tgstation.dme | 2 +
29 files changed, 477 insertions(+), 361 deletions(-)
create mode 100644 code/modules/mob/eye/camera/camera.dm
create mode 100644 code/modules/mob/eye/camera/remote.dm
diff --git a/.github/guides/ISSUE_MANAGER.md b/.github/guides/ISSUE_MANAGER.md
index 3ecab32a7f5..9e5745d7731 100644
--- a/.github/guides/ISSUE_MANAGER.md
+++ b/.github/guides/ISSUE_MANAGER.md
@@ -28,8 +28,8 @@ If an issue reports a runtime, it must have the actual runtime call stack provid
- usr.loc: the floor (150,25,4) (/turf/open/floor/circuit)
- call stack:
- Camera Net (/datum/cameranet): visibility(/list (/list), null, /list (/list), 1)
- - AI (/mob/living/silicon/ai): camera visibility(Inactive AI Eye (/mob/eye/ai_eye))
- - Inactive AI Eye (/mob/eye/ai_eye): setLoc(the floor (150,25,4) (/turf/open/floor/circuit), 0)
+ - AI (/mob/living/silicon/ai): camera visibility(Inactive AI Eye (/mob/eye/camera/ai))
+ - Inactive AI Eye (/mob/eye/camera/ai): setLoc(the floor (150,25,4) (/turf/open/floor/circuit), 0)
- AI (/mob/living/silicon/ai): create eye()
- AI (/mob/living/silicon/ai): Initialize(0, null, TagGamerGame2 (/mob/dead/new_player))
- Atoms (/datum/controller/subsystem/atoms): InitAtom(AI (/mob/living/silicon/ai), 0, /list (/list))
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index b7919353f1a..8548c284ced 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -202,21 +202,23 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
#define isspider(A) (istype(A, /mob/living/basic/spider))
-
-//Misc mobs
-#define isobserver(A) (istype(A, /mob/dead/observer))
-
-#define isdead(A) (istype(A, /mob/dead))
-
-#define isnewplayer(A) (istype(A, /mob/dead/new_player))
+//Eye mobs
+#define iseyemob(A) (istype(A, /mob/eye))
#define isovermind(A) (istype(A, /mob/eye/blob))
-#define issentientdisease(A) (istype(A, /mob/eye/disease))
+#define iscameramob(A) (istype(A, /mob/eye/camera))
-#define iseyemob(A) (istype(A, /mob/eye))
+#define isaicamera(A) (istype(A, /mob/eye/camera/ai))
-#define isaicamera(A) (istype(A, /mob/eye/ai_eye))
+#define isremotecamera(A) (istype(A, /mob/eye/camera/remote))
+
+//Dead mobs
+#define isdead(A) (istype(A, /mob/dead))
+
+#define isobserver(A) (istype(A, /mob/dead/observer))
+
+#define isnewplayer(A) (istype(A, /mob/dead/new_player))
//Objects
#define isobj(A) istype(A, /obj) //override the byond proc because it returns true on children of /atom/movable that aren't objs
diff --git a/code/__HELPERS/view.dm b/code/__HELPERS/view.dm
index 139bdedc425..61aaed88350 100644
--- a/code/__HELPERS/view.dm
+++ b/code/__HELPERS/view.dm
@@ -1,7 +1,5 @@
-/proc/getviewsize(view)
- if(!view) // Just to avoid any runtimes that could otherwise cause constant disconnect loops.
- stack_trace("Missing value for 'view' in getviewsize(), defaulting to world.view!")
- view = world.view
+/proc/getviewsize(view = world.view)
+ SHOULD_BE_PURE(TRUE)
if(isnum(view))
var/totalviewrange = (view < 0 ? -1 : 1) + 2 * view
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index d91eec6e1bc..5ece846e84d 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -59,7 +59,7 @@ GLOBAL_LIST_EMPTY(available_ai_shells)
GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list())) // One for each AI_* status define
GLOBAL_LIST_EMPTY(spidermobs) //all sentient spider mobs
GLOBAL_LIST_EMPTY(bots_list)
-GLOBAL_LIST_EMPTY(aiEyes)
+GLOBAL_LIST_EMPTY(camera_eyes)
GLOBAL_LIST_EMPTY(suit_sensors_list) //all people with suit sensors on
/// All alive mobs with clients.
diff --git a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
index acfa5ee274c..582253e0b92 100644
--- a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
+++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
@@ -362,7 +362,7 @@
/atom/movable/screen/plane_master/camera_static/proc/eye_changed(datum/hud/source, atom/old_eye, atom/new_eye)
SIGNAL_HANDLER
- if(!isaicamera(new_eye))
+ if(!iscameramob(new_eye))
if(!force_hidden)
hide_plane(source.mymob)
return
diff --git a/code/datums/components/supermatter_crystal.dm b/code/datums/components/supermatter_crystal.dm
index e8e6f00ed2f..14183e4beb6 100644
--- a/code/datums/components/supermatter_crystal.dm
+++ b/code/datums/components/supermatter_crystal.dm
@@ -107,7 +107,7 @@
if(iscyborg(user) && atom_source.Adjacent(user))
dust_mob(source, user, cause = "cyborg attack")
return
- if(isaicamera(user))
+ if(iscameramob(user))
return
if(islarva(user))
dust_mob(source, user, cause = "larva attack")
diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm
index 84889ad6bd9..ceaa53e0455 100644
--- a/code/datums/holocall.dm
+++ b/code/datums/holocall.dm
@@ -1,15 +1,16 @@
-/mob/eye/ai_eye/remote/holo/setLoc(turf/destination, force_update = FALSE)
+/mob/eye/camera/remote/holo/setLoc(turf/destination, force_update = FALSE)
// If we're moving outside the space of our projector, then just... don't
- var/obj/machinery/holopad/H = origin
- if(!H?.move_hologram(eye_user, destination))
+ var/obj/machinery/holopad/H = origin_ref?.resolve()
+ if(!H?.move_hologram(user_ref?.resolve(), destination))
sprint = initial(sprint) // Reset sprint so it doesn't balloon in our calling proc
return
return ..()
/obj/machinery/holopad/remove_eye_control(mob/living/user)
- if(user.client)
- user.reset_perspective(null)
- user.remote_control = null
+ var/mob/eye/camera/remote/eye = user.remote_control
+ if(!istype(eye))
+ CRASH("Attempted to remove eye control from non-camera eye. Something has gone horribly wrong.")
+ eye.assign_user(null)
//this datum manages its own references
@@ -24,7 +25,7 @@
var/list/dialed_holopads
///user's eye, once connected
- var/mob/eye/ai_eye/remote/holo/eye
+ var/mob/eye/camera/remote/holo/eye
///user's hologram, once connected
var/obj/effect/overlay/holo_pad_hologram/hologram
///hangup action
@@ -155,15 +156,8 @@
hologram = answering_holopad.activate_holo(user)
hologram.HC = src
- //eyeobj code is horrid, this is the best copypasta I could make
- eye = new
- eye.origin = answering_holopad
- eye.eye_initialized = TRUE
- eye.eye_user = user
- eye.name = "Camera Eye ([user.name])"
- user.remote_control = eye
- user.reset_perspective(eye)
- eye.setLoc(answering_holopad.loc)
+ eye = new(get_turf(answering_holopad), answering_holopad)
+ eye.assign_user(user)
hangup = new(eye, src)
hangup.Grant(user)
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index 19dbc2de330..b6c5b3959ca 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -881,7 +881,7 @@
var/extra_context = ""
var/used_name = name
- if(isliving(user) || isovermind(user) || isaicamera(user) || (ghost_screentips && isobserver(user)))
+ if(isliving(user) || isovermind(user) || iscameramob(user) || (ghost_screentips && isobserver(user)))
var/obj/item/held_item = user.get_active_held_item()
if (user.mob_flags & MOB_HAS_SCREENTIPS_NAME_OVERRIDE)
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index faf1c896946..cbaf94e6583 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -73,7 +73,7 @@
. = ..()
if(!new_viewer || hud_users_all_z_levels.len != 1)
return
- for(var/mob/eye/ai_eye/eye as anything in GLOB.aiEyes)
+ for(var/mob/eye/camera/ai/eye as anything in GLOB.camera_eyes)
eye.update_ai_detect_hud()
/datum/atom_hud/data/malf_apc
diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm
index 80a06fa6b9f..5633fe49047 100644
--- a/code/game/machinery/computer/camera_advanced.dm
+++ b/code/game/machinery/computer/camera_advanced.dm
@@ -8,7 +8,7 @@
var/list/z_lock = list() // Lock use to these z levels
var/lock_override = NONE
- var/mob/eye/ai_eye/remote/eyeobj
+ var/mob/eye/camera/remote/eyeobj
var/mob/living/current_user = null
var/list/networks = list(CAMERANET_NETWORK_SS13)
/// Typepath of the action button we use as "off"
@@ -76,27 +76,33 @@
/obj/machinery/computer/camera_advanced/syndie/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
return //For syndie nuke shuttle, to spy for station.
+/**
+ * Initializes a camera eye.
+ * Returns TRUE if initialization was successful.
+ * Will return nothing if it runtimes.
+ */
/obj/machinery/computer/camera_advanced/proc/CreateEye()
- eyeobj = new()
- eyeobj.origin = src
+ if(eyeobj)
+ CRASH("Tried to make another eyeobj for some reason. Why?")
+
+ eyeobj = new(get_turf(src), src)
+ return TRUE
/obj/machinery/computer/camera_advanced/proc/GrantActions(mob/living/user)
for(var/datum/action/to_grant as anything in actions)
to_grant.Grant(user)
/obj/machinery/proc/remove_eye_control(mob/living/user)
- CRASH("[type] does not implement ai eye handling")
+ CRASH("[type] does not implement camera eye handling")
/obj/machinery/computer/camera_advanced/proc/give_eye_control(mob/user)
if(isnull(user?.client))
return
- GrantActions(user)
+
current_user = user
- eyeobj.eye_user = user
- eyeobj.name = "Camera Eye ([user.name])"
- user.remote_control = eyeobj
- user.reset_perspective(eyeobj)
- eyeobj.setLoc(eyeobj.loc)
+ eyeobj.assign_user(user)
+ GrantActions(user)
+
if(should_supress_view_changes)
user.client.view_size.supress()
begin_processing()
@@ -110,14 +116,11 @@
for(var/datum/camerachunk/camerachunks_gone as anything in eyeobj.visibleCameraChunks)
camerachunks_gone.remove(eyeobj)
- user.reset_perspective(null)
- if(eyeobj.visible_icon)
- user.client.images -= eyeobj.user_image
+ eyeobj.assign_user(null)
+ current_user = null
+
user.client.view_size.unsupress()
- eyeobj.eye_user = null
- user.remote_control = null
- current_user = null
playsound(src, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE)
/obj/machinery/computer/camera_advanced/on_set_is_operational(old_value)
@@ -148,39 +151,43 @@
if(!QDELETED(current_user))
to_chat(user, span_warning("The console is already in use!"))
return
- var/mob/living/L = user
- if(!eyeobj)
- CreateEye()
- if(!eyeobj) //Eye creation failed
+
+ if(eyeobj)
+ give_eye_control(user)
+ eyeobj.setLoc(eyeobj.loc)
return
- if(!eyeobj.eye_initialized)
- var/camera_location
- var/turf/myturf = get_turf(src)
- if(eyeobj.use_static != FALSE)
- if((!length(z_lock) || (myturf.z in z_lock)) && GLOB.cameranet.checkTurfVis(myturf))
- camera_location = myturf
- else
- for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras)
- if(!C.can_use() || length(z_lock) && !(C.z in z_lock))
- continue
- var/list/network_overlap = networks & C.network
- if(length(network_overlap))
- camera_location = get_turf(C)
- break
+ /* We're attempting to initialize the eye past this point */
+
+ if(!CreateEye())
+ to_chat(user, span_warning("\The [src] flashes a bunch of never-ending errors on the display. Something is really wrong."))
+ return
+
+ var/camera_location
+ var/turf/myturf = get_turf(src)
+ var/consider_zlock = (!!length(z_lock))
+
+ if(!eyeobj.use_visibility)
+ if(consider_zlock && !(myturf.z in z_lock))
+ camera_location = locate(round(world.maxx * 0.5), round(world.maxy * 0.5), z_lock[1])
else
camera_location = myturf
- if(length(z_lock) && !(myturf.z in z_lock))
- camera_location = locate(round(world.maxx/2), round(world.maxy/2), z_lock[1])
-
- if(camera_location)
- eyeobj.eye_initialized = TRUE
- give_eye_control(L)
- eyeobj.setLoc(camera_location)
- else
- unset_machine()
else
- give_eye_control(L)
- eyeobj.setLoc(eyeobj.loc)
+ if((!consider_zlock || (myturf.z in z_lock)) && GLOB.cameranet.checkTurfVis(myturf))
+ camera_location = myturf
+ else
+ for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras)
+ if(!C.can_use() || consider_zlock && !(C.z in z_lock))
+ continue
+ var/list/network_overlap = networks & C.network
+ if(length(network_overlap))
+ camera_location = get_turf(C)
+ break
+
+ if(camera_location)
+ give_eye_control(user)
+ eyeobj.setLoc(camera_location, TRUE)
+ else
+ unset_machine()
/obj/machinery/computer/camera_advanced/attack_robot(mob/user)
return attack_hand(user)
@@ -188,73 +195,6 @@
/obj/machinery/computer/camera_advanced/attack_ai(mob/user)
return //AIs would need to disable their own camera procs to use the console safely. Bugs happen otherwise.
-/mob/eye/ai_eye/remote
- name = "Inactive Camera Eye"
- ai_detector_visible = FALSE
- var/sprint = 10
- var/cooldown = 0
- var/acceleration = 1
- var/mob/living/eye_user = null
- var/obj/machinery/origin
- var/eye_initialized = 0
- var/visible_icon = 0
- var/image/user_image = null
-
-/mob/eye/ai_eye/remote/update_remote_sight(mob/living/user)
- user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras
- user.set_sight(SEE_TURFS)
- return TRUE
-
-/mob/eye/ai_eye/remote/Destroy()
- if(origin && eye_user)
- origin.remove_eye_control(eye_user,src)
- origin = null
- . = ..()
- eye_user = null
-
-/mob/eye/ai_eye/remote/GetViewerClient()
- if(eye_user)
- return eye_user.client
- return null
-
-/mob/eye/ai_eye/remote/setLoc(turf/destination, force_update = FALSE)
- if(eye_user)
- destination = get_turf(destination)
- if (destination)
- abstract_move(destination)
- else
- moveToNullspace()
-
- update_ai_detect_hud()
-
- if(use_static)
- GLOB.cameranet.visibility(src, GetViewerClient(), null, use_static)
-
- if(visible_icon)
- if(eye_user.client)
- eye_user.client.images -= user_image
- user_image = image(icon,loc,icon_state, FLY_LAYER)
- SET_PLANE(user_image, ABOVE_GAME_PLANE, destination)
- eye_user.client.images += user_image
-
-/mob/eye/ai_eye/remote/relaymove(mob/living/user, direction)
- var/initial = initial(sprint)
- var/max_sprint = 50
-
- if(cooldown && cooldown < world.timeofday) // 3 seconds
- sprint = initial
-
- for(var/i = 0; i < max(sprint, initial); i += 20)
- var/turf/step = get_turf(get_step(src, direction))
- if(step)
- setLoc(step)
-
- cooldown = world.timeofday + 5
- if(acceleration)
- sprint = min(sprint + 0.5, max_sprint)
- else
- sprint = initial
-
/datum/action/innate/camera_off
name = "End Camera View"
button_icon = 'icons/mob/actions/actions_silicon.dmi'
@@ -263,8 +203,8 @@
/datum/action/innate/camera_off/Activate()
if(!owner || !isliving(owner))
return
- var/mob/eye/ai_eye/remote/remote_eye = owner.remote_control
- var/obj/machinery/computer/camera_advanced/console = remote_eye.origin
+ var/mob/eye/camera/remote/remote_eye = owner.remote_control
+ var/obj/machinery/computer/camera_advanced/console = remote_eye.origin_ref.resolve()
console.remove_eye_control(owner)
/datum/action/innate/camera_jump
@@ -275,8 +215,8 @@
/datum/action/innate/camera_jump/Activate()
if(!owner || !isliving(owner))
return
- var/mob/eye/ai_eye/remote/remote_eye = owner.remote_control
- var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin
+ var/mob/eye/camera/remote/remote_eye = owner.remote_control
+ var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin_ref.resolve()
var/list/L = list()
@@ -320,7 +260,7 @@
/datum/action/innate/camera_multiz_up/Activate()
if(!owner || !isliving(owner))
return
- var/mob/eye/ai_eye/remote/remote_eye = owner.remote_control
+ var/mob/eye/camera/remote/remote_eye = owner.remote_control
if(remote_eye.zMove(UP))
to_chat(owner, span_notice("You move upwards."))
else
@@ -334,7 +274,7 @@
/datum/action/innate/camera_multiz_down/Activate()
if(!owner || !isliving(owner))
return
- var/mob/eye/ai_eye/remote/remote_eye = owner.remote_control
+ var/mob/eye/camera/remote/remote_eye = owner.remote_control
if(remote_eye.zMove(DOWN))
to_chat(owner, span_notice("You move downwards."))
else
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index e323c65b1eb..0ea1e867b4c 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -204,7 +204,7 @@
var/turf/our_turf = get_turf(src)
detect_state = PROXIMITY_NONE
- for(var/mob/eye/ai_eye/AI_eye as anything in GLOB.aiEyes)
+ for(var/mob/eye/camera/ai/AI_eye as anything in GLOB.camera_eyes)
if(!AI_eye.ai_detector_visible)
continue
@@ -253,7 +253,7 @@
// copied from camera chunks but we are doing a really big edge case here though
/obj/item/multitool/ai_detect/proc/surrounding_chunks(turf/epicenter)
. = list()
- var/static_range = /mob/eye/ai_eye::static_visibility_range
+ var/static_range = /mob/eye/camera/ai::static_visibility_range
var/x1 = max(1, epicenter.x - static_range)
var/y1 = max(1, epicenter.y - static_range)
var/x2 = min(world.maxx, epicenter.x + static_range)
diff --git a/code/game/objects/structures/construction_console/construction_actions.dm b/code/game/objects/structures/construction_console/construction_actions.dm
index b3a4e309ffa..cce7d2ee516 100644
--- a/code/game/objects/structures/construction_console/construction_actions.dm
+++ b/code/game/objects/structures/construction_console/construction_actions.dm
@@ -5,7 +5,7 @@
/datum/action/innate/construction
button_icon = 'icons/mob/actions/actions_construction.dmi'
///Console's eye mob
- var/mob/eye/ai_eye/remote/base_construction/remote_eye
+ var/mob/eye/camera/remote/base_construction/remote_eye
///Console itself
var/obj/machinery/computer/camera_advanced/base_construction/base_console
///Is this used to build only on the station z level?
diff --git a/code/game/objects/structures/construction_console/construction_console.dm b/code/game/objects/structures/construction_console/construction_console.dm
index 97b83acccae..69275d0e951 100644
--- a/code/game/objects/structures/construction_console/construction_console.dm
+++ b/code/game/objects/structures/construction_console/construction_console.dm
@@ -1,7 +1,7 @@
/**
* Camera console used to control a base building drone
*
- * Using this console will put the user in control of a [base building drone][/mob/eye/ai_eye/remote/base_construction].
+ * Using this console will put the user in control of a [base building drone][/mob/eye/camera/remote/base_construction].
* The drone will appear somewhere within the allowed_area var, or if no area is specified, at the location of the console.area
* Upon interacting, the user will be granted a set of base building actions that will generally be carried out at the drone's location.
* To create a new base builder system, this class should be the only thing that needs to be subtyped.
@@ -61,8 +61,7 @@
var/turf/spawn_spot = find_spawn_spot()
if (!spawn_spot)
return FALSE
- eyeobj = new /mob/eye/ai_eye/remote/base_construction(spawn_spot, src)
- eyeobj.origin = src
+ eyeobj = new /mob/eye/camera/remote/base_construction(spawn_spot, src)
return TRUE
/obj/machinery/computer/camera_advanced/base_construction/attackby(obj/item/W, mob/user, params)
@@ -95,7 +94,7 @@
* The mob is constrained to a given area defined by the base construction console.
*
*/
-/mob/eye/ai_eye/remote/base_construction
+/mob/eye/camera/remote/base_construction
name = "construction holo-drone"
//Allows any curious crew to watch the base after it leaves. (This is safe as the base cannot be modified once it leaves)
move_on_shuttle = TRUE
@@ -105,20 +104,20 @@
///Reference to the camera console controlling this drone
var/obj/machinery/computer/camera_advanced/base_construction/linked_console
-/mob/eye/ai_eye/remote/base_construction/Initialize(mapload, obj/machinery/computer/camera_advanced/console_link)
+/mob/eye/camera/remote/base_construction/Initialize(mapload, obj/machinery/computer/camera_advanced/console_link)
linked_console = console_link
if(!linked_console)
stack_trace("A base consturuction drone was created with no linked console")
return INITIALIZE_HINT_QDEL
return ..()
-/mob/eye/ai_eye/remote/base_construction/setLoc(turf/destination, force_update = FALSE)
+/mob/eye/camera/remote/base_construction/setLoc(turf/destination, force_update = FALSE)
var/area/curr_area = get_area(destination)
//Only move if we're in the allowed area. If no allowed area is defined, then we're free to move wherever.
if(!linked_console.allowed_area || istype(curr_area, linked_console.allowed_area))
return ..()
-/mob/eye/ai_eye/remote/base_construction/relaymove(mob/living/user, direction)
+/mob/eye/camera/remote/base_construction/relaymove(mob/living/user, direction)
//This camera eye is visible, and as such needs to keep its dir updated
dir = direction
return ..()
diff --git a/code/modules/antagonists/abductor/machinery/camera.dm b/code/modules/antagonists/abductor/machinery/camera.dm
index f4ddd345075..644e3a827bf 100644
--- a/code/modules/antagonists/abductor/machinery/camera.dm
+++ b/code/modules/antagonists/abductor/machinery/camera.dm
@@ -21,11 +21,12 @@
return ..()
/obj/machinery/computer/camera_advanced/abductor/CreateEye()
- ..()
- eyeobj.visible_icon = TRUE
+ . = ..()
+ //For observers
eyeobj.icon = 'icons/mob/eyemob.dmi'
eyeobj.icon_state = "abductor_camera"
- eyeobj.SetInvisibility(INVISIBILITY_OBSERVER)
+ //For the user
+ eyeobj.set_user_icon(eyeobj.icon, eyeobj.icon_state)
/obj/machinery/computer/camera_advanced/abductor/GrantActions(mob/living/carbon/user)
if(!abduct_created)
@@ -57,7 +58,7 @@
to_chat(owner, span_warning("You must wait [DisplayTimeText(use_delay - world.time)] to use the [target] again!"))
return
var/mob/living/carbon/human/C = owner
- var/mob/eye/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/eye/camera/remote/remote_eye = C.remote_control
var/obj/machinery/abductor/pad/P = target
var/area/target_area = get_area(remote_eye)
@@ -101,7 +102,7 @@
to_chat(owner, span_warning("You can only teleport to one place at a time!"))
return
var/mob/living/carbon/human/C = owner
- var/mob/eye/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/eye/camera/remote/remote_eye = C.remote_control
var/obj/machinery/abductor/pad/P = target
var/area/target_area = get_area(remote_eye)
@@ -151,7 +152,7 @@
return
var/mob/living/carbon/human/C = owner
- var/mob/eye/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/eye/camera/remote/remote_eye = C.remote_control
var/obj/machinery/abductor/console/console = target
console.SetDroppoint(remote_eye.loc,owner)
diff --git a/code/modules/forensics/_forensics.dm b/code/modules/forensics/_forensics.dm
index 8058a32e7fa..450521e6c62 100644
--- a/code/modules/forensics/_forensics.dm
+++ b/code/modules/forensics/_forensics.dm
@@ -112,7 +112,7 @@
if(!iseyemob(suspect))
return
if(isaicamera(suspect))
- var/mob/eye/ai_eye/ai_camera = suspect
+ var/mob/eye/camera/ai/ai_camera = suspect
if(!ai_camera.ai)
return
suspect = ai_camera.ai
@@ -193,7 +193,7 @@
if(!iseyemob(suspect))
return
if(isaicamera(suspect))
- var/mob/eye/ai_eye/ai_camera = suspect
+ var/mob/eye/camera/ai/ai_camera = suspect
if(!ai_camera.ai)
return
suspect = ai_camera.ai
diff --git a/code/modules/mob/eye/camera/camera.dm b/code/modules/mob/eye/camera/camera.dm
new file mode 100644
index 00000000000..9f3ddc7b2ee
--- /dev/null
+++ b/code/modules/mob/eye/camera/camera.dm
@@ -0,0 +1,80 @@
+/**
+ * Eye mob used to look around a [camera network][/datum/cameranet]. \
+ * As it moves, it makes requests to the network to update what the user can and cannot see.
+ */
+/mob/eye/camera
+ name = "Inactive Camera Eye"
+ icon = 'icons/mob/eyemob.dmi'
+ icon_state = "generic_camera"
+
+ invisibility = INVISIBILITY_OBSERVER
+ interaction_range = INFINITY
+ /// If TRUE, the eye will cover turfs hidden to the cameranet with static.
+ var/use_visibility = TRUE
+ /// List of [camera chunks][/datum/camerachunk] visible to this camera.
+ /// Please don't interface with this directly. Use the [cameranet][/datum/cameranet].
+ var/final/list/datum/camerachunk/visibleCameraChunks = list()
+ /// NxN Range of a single camera chunk.
+ var/static_visibility_range = 16
+
+/mob/eye/camera/Initialize(mapload)
+ . = ..()
+ GLOB.camera_eyes += src
+
+/mob/eye/camera/Destroy()
+ for(var/datum/camerachunk/chunk in visibleCameraChunks)
+ chunk.remove(src)
+ GLOB.camera_eyes -= src
+ return ..()
+
+/**
+ * Getter proc for getting the current user's client.
+ *
+ * The base version of this proc returns null.
+ * Subtypes are expected to overload this proc and make it return something meaningful.
+ */
+/mob/eye/camera/proc/GetViewerClient()
+ RETURN_TYPE(/client)
+ SHOULD_BE_PURE(TRUE)
+
+ return null
+
+/**
+ * Use this when setting the camera eye's location directly. \
+ * It will also attempt to update visible chunks.
+ */
+/mob/eye/camera/proc/setLoc(destination, force_update = FALSE)
+ SHOULD_NOT_SLEEP(TRUE)
+ SHOULD_CALL_PARENT(TRUE)
+
+ destination = get_turf(destination)
+ if(!force_update && (destination == get_turf(src)))
+ return
+
+ if(destination)
+ abstract_move(destination)
+ else
+ moveToNullspace()
+
+ if(use_visibility)
+ update_visibility()
+ update_parallax_contents()
+
+/// Sends a visibility query to the cameranet.
+/// Can be used as a signal handler.
+/mob/eye/camera/proc/update_visibility()
+ SIGNAL_HANDLER
+ PROTECTED_PROC(TRUE)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(use_visibility)
+ GLOB.cameranet.visibility(src)
+
+/mob/eye/camera/zMove(dir, turf/target, z_move_flags = NONE, recursions_left = 1, list/falling_movs)
+ . = ..()
+ if(.)
+ setLoc(loc, force_update = TRUE)
+
+/mob/eye/camera/Move()
+ SHOULD_NOT_OVERRIDE(TRUE)
+ return
diff --git a/code/modules/mob/eye/camera/remote.dm b/code/modules/mob/eye/camera/remote.dm
new file mode 100644
index 00000000000..97fe41886ce
--- /dev/null
+++ b/code/modules/mob/eye/camera/remote.dm
@@ -0,0 +1,130 @@
+/**
+ * A camera controlled by a machine-operating user, like advanced cameras.
+ * Handles assigning/unassigning it's users, as well as applying sight effects.
+ */
+/mob/eye/camera/remote
+ /// Weakref to the current user of this eye. Must be a [living mob][/mob/living].
+ var/datum/weakref/user_ref
+ /// Weakref to the creator of this eye. Must be a [machine][/obj/machinery].
+ var/datum/weakref/origin_ref
+
+ /// TRUE if this camera should show itself to the user.
+ var/visible_to_user = FALSE
+ /// If visible_to_user is TRUE, it will show this in the center of the screen.
+ VAR_PROTECTED/image/user_image
+
+ /* The below code could be shared by AI eyes... */
+
+ /// If TRUE, the eye will have acceleration when moving.
+ var/acceleration = TRUE
+ /// Used internally for calculating wait time. (world.timeofday + wait_time)
+ VAR_FINAL/last_moved = 0
+ /// The amount of time that must pass before var/sprint is reset.
+ VAR_PROTECTED/wait_time = 5 DECISECONDS
+ /// The speed of the camera. Scales from initial(sprint) to var/max_sprint
+ VAR_PROTECTED/sprint = 10
+ /// Amount of speed that is added to var/sprint.
+ VAR_PROTECTED/momentum = 0.5
+ /// The maximum sprint that this eye can reach.
+ VAR_PROTECTED/max_sprint = 50
+
+
+/mob/eye/camera/remote/Initialize(mapload, obj/machinery/creator)
+ if(!creator)
+ return INITIALIZE_HINT_QDEL
+
+ . = ..()
+
+ origin_ref = WEAKREF(creator)
+ if(visible_to_user)
+ set_user_icon(icon, icon_state)
+
+/mob/eye/camera/remote/Destroy()
+ var/mob/living/user = user_ref?.resolve()
+ var/obj/machinery/origin = origin_ref?.resolve()
+ if(origin && user)
+ origin.remove_eye_control(user,src)
+
+ assign_user(null)
+ origin_ref = null
+ return ..()
+
+/mob/eye/camera/remote/proc/assign_user(mob/living/new_user)
+ var/mob/living/old_user = user_ref?.resolve()
+ if(old_user)
+ old_user.remote_control = null
+ old_user.reset_perspective(null)
+ name = initial(src.name)
+
+ var/client/old_user_client = GetViewerClient()
+ if(user_image && old_user_client)
+ old_user_client.images -= user_image
+
+ user_ref = WEAKREF(new_user) //The user_ref can still be null!
+
+ if(new_user)
+ new_user.remote_control = src
+ new_user.reset_perspective(src)
+ name = "Camera Eye ([new_user.name])"
+
+ var/client/new_user_client = GetViewerClient()
+ if(user_image && new_user_client)
+ new_user_client.images += user_image
+
+/**
+ * Sets the camera's user image to this icon and state.
+ * If chosen_icon is null, the user image will be removed.
+ */
+/mob/eye/camera/remote/proc/set_user_icon(icon/chosen_icon, icon_state)
+ SHOULD_CALL_PARENT(TRUE)
+
+ var/client/user_client = GetViewerClient()
+
+ if(!isnull(chosen_icon))
+ set_user_icon(null) //remove whatever the last icon was
+ if(!isicon(chosen_icon) || !(!isnull(icon_state) && istext(icon_state)))
+ CRASH("Tried to set [src]'s user_image with bad parameters")
+
+ user_image = image(chosen_icon, src, icon_state, FLY_LAYER)
+ if(user_client)
+ user_client.images += user_image
+ else
+ if(user_client)
+ user_client.images -= user_image
+ QDEL_NULL(user_image)
+
+/mob/eye/camera/remote/update_remote_sight(mob/living/user)
+ user.set_invis_see(SEE_INVISIBLE_LIVING) //can't see ghosts through cameras
+ user.set_sight(SEE_TURFS)
+ return TRUE
+
+/mob/eye/camera/remote/GetViewerClient()
+ var/mob/living/user = user_ref?.resolve()
+
+ if(user)
+ return user.client
+ return null
+
+/mob/eye/camera/remote/setLoc(turf/destination, force_update = FALSE)
+ . = ..()
+
+ var/client/user_client = GetViewerClient()
+ if(user_image && user_client)
+ SET_PLANE(user_image, ABOVE_GAME_PLANE, destination) //incase we move a z-level
+
+/mob/eye/camera/remote/relaymove(mob/living/user, direction)
+ var/initial = initial(src.sprint)
+
+ if(last_moved < world.timeofday) // It's been too long since we last moved, reset sprint
+ sprint = initial
+
+ for(var/i = 0; i < max(sprint, initial); i += 20)
+ var/turf/step = get_turf(get_step(src, direction))
+ if(step)
+ setLoc(step)
+
+ last_moved = world.timeofday + wait_time
+ if(acceleration)
+ sprint = min(sprint + momentum, max_sprint)
+ else
+ sprint = initial
diff --git a/code/modules/mob/eye/eye.dm b/code/modules/mob/eye/eye.dm
index ca58e2969c5..54cb5e2b40a 100644
--- a/code/modules/mob/eye/eye.dm
+++ b/code/modules/mob/eye/eye.dm
@@ -1,4 +1,4 @@
-// Eye mob, used by cameras and overminds such as blobs.
+/// Eye mob, used by cameras and overminds such as blobs.
/mob/eye
name = "eye mob"
density = FALSE
@@ -8,9 +8,9 @@
invisibility = INVISIBILITY_ABSTRACT // No one can see us
sight = SEE_SELF
status_flags = NONE
- /// Toggles if the camera can move on shuttles
+ /// Toggles if the eye can move on shuttles
var/move_on_shuttle = FALSE
- /// Toggles if the camera can use emotes
+ /// Toggles if the eye can use emotes
var/has_emotes = FALSE
/mob/eye/Initialize(mapload)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index b8e755faade..46cfb5980a2 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -62,7 +62,7 @@
var/nuking = FALSE
var/obj/machinery/doomsday_device/doomsday_device
- var/mob/eye/ai_eye/eyeobj
+ var/mob/eye/camera/ai/eyeobj
var/sprint = 10
var/last_moved = 0
var/acceleration = TRUE
@@ -1139,8 +1139,8 @@
target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct
. = ..()
-/mob/living/silicon/ai/proc/camera_visibility(mob/eye/ai_eye/moved_eye)
- GLOB.cameranet.visibility(moved_eye, client, all_eyes, TRUE)
+/mob/living/silicon/ai/proc/camera_visibility(mob/eye/camera/ai/moved_eye)
+ GLOB.cameranet.visibility(moved_eye)
/mob/living/silicon/ai/forceMove(atom/destination)
. = ..()
diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
index b8bd1f443ea..1f8c452f492 100644
--- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
@@ -12,7 +12,6 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
var/list/obj/machinery/camera/cameras = list()
/// The chunks of the map, mapping the areas that the cameras can see.
var/list/chunks = list()
- var/ready = 0
/// List of images cloned by all chunk static images put onto turfs cameras cant see
/// Indexed by the plane offset to use
@@ -56,16 +55,12 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
if(!.)
chunks[key] = . = new /datum/camerachunk(x, y, lowest.z)
-/// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or its location is set.
-/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes, use_static = TRUE)
+/// Updates what the camera eye can see. It is recommended you use this when a camera eye moves or its location is set.
+/datum/cameranet/proc/visibility(list/moved_eyes)
if(!islist(moved_eyes))
moved_eyes = moved_eyes ? list(moved_eyes) : list()
- if(islist(other_eyes))
- other_eyes = (other_eyes - moved_eyes)
- else
- other_eyes = list()
- for(var/mob/eye/ai_eye/eye as anything in moved_eyes)
+ for(var/mob/eye/camera/eye as anything in moved_eyes)
var/list/visibleChunks = list()
//Get the eye's turf in case its located in an object like a mecha
var/turf/eye_turf = get_turf(eye)
@@ -129,6 +124,8 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
* to change the time between static updates.
*/
/datum/cameranet/proc/majorChunkChange(atom/c, choice, update_delay_buffer)
+ PROTECTED_PROC(TRUE)
+
if(QDELETED(c) && choice == 1)
CRASH("Tried to add a qdeleting camera to the net")
diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
index 7b2c57abc3c..73908f162b8 100644
--- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
@@ -1,9 +1,9 @@
#define UPDATE_BUFFER_TIME (2.5 SECONDS)
-// CAMERA CHUNK
-//
-// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
-// Allows the AI Eye to stream these chunks and know what it can and cannot see.
+/**
+ * A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. \
+ * Allows Camera Eyes to stream these chunks and know what it can and cannot see.
+ */
/datum/camerachunk
///turfs our cameras cant see but are inside our grid. associative list of the form: list(obscured turf = static image on that turf)
@@ -16,7 +16,7 @@
///list of all turfs, associative with that turf's static image
///turf -> /image
var/list/turfs = list()
- ///eye mobs that can see turfs in our grid
+ ///Camera mobs that can see turfs in our grid
var/list/seenby = list()
///images currently in use on obscured turfs.
var/list/active_static_images = list()
@@ -27,24 +27,24 @@
var/lower_z
var/upper_z
-/// Add an AI eye to the chunk, then update if changed.
-/datum/camerachunk/proc/add(mob/eye/ai_eye/eye)
+/// Add a camera eye to the chunk, then update if changed.
+/datum/camerachunk/proc/add(mob/eye/camera/eye)
eye.visibleCameraChunks += src
seenby += eye
if(changed)
update()
var/client/client = eye.GetViewerClient()
- if(client && eye.use_static)
+ if(client && eye.use_visibility)
client.images += active_static_images
-/// Remove an AI eye from the chunk
-/datum/camerachunk/proc/remove(mob/eye/ai_eye/eye, remove_static_with_last_chunk = TRUE)
+/// Remove a camera eye from the chunk
+/datum/camerachunk/proc/remove(mob/eye/camera/ai/eye)
eye.visibleCameraChunks -= src
seenby -= eye
var/client/client = eye.GetViewerClient()
- if(client && eye.use_static)
+ if(client && eye.use_visibility)
client.images -= active_static_images
/// Called when a chunk has changed. I.E: A wall was deleted.
@@ -56,6 +56,7 @@
/**
* Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will
* instead be flagged to update the next time an AI Eye moves near it.
+ *
* update_delay_buffer is used for cameras that are moving around, which are cyborg inbuilt cameras and
* mecha onboard cameras. This buffer should be usually lower than UPDATE_BUFFER_TIME because
* otherwise a moving camera can run out of its own view before updating static.
@@ -89,8 +90,8 @@
///turfs that we could see last update but cant see now
var/list/newly_obscured_turfs = visibleTurfs - updated_visible_turfs
- for(var/mob/eye/ai_eye/client_eye as anything in seenby)
- var/client/client = client_eye.ai?.client || client_eye.client
+ for(var/mob/eye/camera/client_eye as anything in seenby)
+ var/client/client = client_eye.GetViewerClient()
if(!client)
continue
@@ -119,8 +120,8 @@
changed = FALSE
- for(var/mob/eye/ai_eye/client_eye as anything in seenby)
- var/client/client = client_eye.ai?.client || client_eye.client
+ for(var/mob/eye/camera/client_eye as anything in seenby)
+ var/client/client = client_eye.GetViewerClient()
if(!client)
continue
diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm
index c93054d6d26..25ccafe26c0 100644
--- a/code/modules/mob/living/silicon/ai/freelook/eye.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm
@@ -1,43 +1,50 @@
-// AI EYE
-//
-// An invisible (no icon) mob that the AI controls to look around the station with.
-// It streams chunks as it moves around, which will show it what the AI can and cannot see.
-/mob/eye/ai_eye
+/mob/eye/camera/ai
name = "Inactive AI Eye"
-
icon_state = "ai_camera"
- icon = 'icons/mob/eyemob.dmi'
- invisibility = INVISIBILITY_MAXIMUM
+
hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
- var/list/visibleCameraChunks = list()
+ /// The AI who owns this eye.
var/mob/living/silicon/ai/ai = null
+ /// Whether this eye will transmit speech near it to the AI.
var/relay_speech = FALSE
- var/use_static = TRUE
- var/static_visibility_range = 16
+ /// Whether this eye can be found with AI detectors.
var/ai_detector_visible = TRUE
+ /// The color of the area if the eye is detectable.
var/ai_detector_color = COLOR_RED
- interaction_range = INFINITY
-/mob/eye/ai_eye/Initialize(mapload)
+/mob/eye/camera/ai/Initialize(mapload)
. = ..()
- GLOB.aiEyes += src
- update_ai_detect_hud()
- setLoc(loc, TRUE)
-
-/mob/eye/ai_eye/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
- . = ..()
- if(same_z_layer)
- return
update_ai_detect_hud()
-/mob/eye/ai_eye/examine(mob/user) //Displays a silicon's laws to ghosts
- . = ..()
- if(istype(ai) && ai.laws && isobserver(user))
- . += "[ai] has the following laws:"
- for(var/law in ai.laws.get_law_list(include_zeroth = TRUE))
- . += law
+/mob/eye/camera/ai/Destroy()
+ if(ai)
+ ai.all_eyes -= src
+ ai = null
+ if(ai_detector_visible)
+ var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
+ hud.remove_atom_from_hud(src)
+ var/list/L = hud_list[AI_DETECT_HUD]
+ QDEL_LIST(L)
+ return ..()
-/mob/eye/ai_eye/proc/update_ai_detect_hud()
+/**
+ * Returns a list of turfs visible to the client's viewsize. \
+ * Note that this will return an empty list if the camera's loc is not a turf.
+ */
+/mob/eye/camera/ai/proc/get_visible_turfs()
+ RETURN_TYPE(/list/turf)
+ SHOULD_BE_PURE(TRUE)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!isturf(loc))
+ return list()
+ var/client/C = GetViewerClient()
+ var/view = C ? getviewsize(C.view) : getviewsize(world.view)
+ var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
+ var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
+ return block(lowerleft, upperright)
+
+/mob/eye/camera/ai/proc/update_ai_detect_hud()
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
var/list/old_images = hud_list[AI_DETECT_HUD]
if(!ai_detector_visible)
@@ -75,42 +82,17 @@
active_hud_list[AI_DETECT_HUD] = new_images
hud.add_atom_to_hud(src)
-/mob/eye/ai_eye/proc/get_visible_turfs()
- if(!isturf(loc))
- return list()
- var/client/C = GetViewerClient()
- var/view = C ? getviewsize(C.view) : getviewsize(world.view)
- var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
- var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
- return block(lowerleft, upperright)
-
-/// Used in cases when the eye is located in a movable object (i.e. mecha)
-/mob/eye/ai_eye/proc/update_visibility()
- SIGNAL_HANDLER
- if(use_static)
- ai.camera_visibility(src)
-
-// Use this when setting the aiEye's location.
-// It will also stream the chunk that the new loc is in.
-
-/mob/eye/ai_eye/proc/setLoc(destination, force_update = FALSE)
+/mob/eye/camera/ai/setLoc(destination, force_update = FALSE)
if(!ai)
return
if(!isturf(ai.loc))
return
- destination = get_turf(destination)
- if(!force_update && (destination == get_turf(src)))
- return //we are already here!
- if (destination)
- abstract_move(destination)
- else
- moveToNullspace()
- if(use_static)
- ai.camera_visibility(src)
+
+ . = ..()
+
if(ai.client && !ai.multicam_on)
ai.client.set_eye(src)
update_ai_detect_hud()
- update_parallax_contents()
//Holopad
if(istype(ai.current, /obj/machinery/holopad))
var/obj/machinery/holopad/H = ai.current
@@ -122,33 +104,31 @@
if(ai.master_multicam)
ai.master_multicam.refresh_view()
-/mob/eye/ai_eye/zMove(dir, turf/target, z_move_flags = NONE, recursions_left = 1, list/falling_movs)
- . = ..()
- if(.)
- setLoc(loc, force_update = TRUE)
+/mob/eye/camera/ai/update_visibility()
+ if(ai)
+ ai.camera_visibility(src)
+ else
+ ..()
-/mob/eye/ai_eye/Move()
- return
-
-/mob/eye/ai_eye/proc/GetViewerClient()
+/mob/eye/camera/ai/GetViewerClient()
if(ai)
return ai.client
return null
-/mob/eye/ai_eye/Destroy()
- if(ai)
- ai.all_eyes -= src
- ai = null
- for(var/V in visibleCameraChunks)
- var/datum/camerachunk/c = V
- c.remove(src)
- GLOB.aiEyes -= src
- if(ai_detector_visible)
- var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
- hud.remove_atom_from_hud(src)
- var/list/L = hud_list[AI_DETECT_HUD]
- QDEL_LIST(L)
- return ..()
+/mob/eye/camera/ai/examine(mob/user) //Displays a silicon's laws to ghosts
+ . = ..()
+ if(istype(ai) && ai.laws && isobserver(user))
+ . += "[ai] has the following laws:"
+ for(var/law in ai.laws.get_law_list(include_zeroth = TRUE))
+ . += law
+
+/mob/eye/camera/ai/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
+ . = ..()
+ if(same_z_layer)
+ return
+ update_ai_detect_hud()
+
+/*----------------------------------------------------*/
/atom/proc/move_camera_by_click()
if(!isAI(usr))
@@ -189,7 +169,6 @@
sprint = initial(sprint)
ai_tracking_tool.reset_tracking()
-
#undef SPRINT_PER_STEP
#undef MAX_SPRINT
#undef SPRINT_PER_TICK
@@ -215,12 +194,11 @@
/mob/living/silicon/ai/proc/create_eye()
if(eyeobj)
return
- eyeobj = new /mob/eye/ai_eye()
+ eyeobj = new /mob/eye/camera/ai()
all_eyes += eyeobj
eyeobj.ai = src
- eyeobj.setLoc(loc)
eyeobj.name = "[name] (AI Eye)"
- eyeobj.real_name = eyeobj.name
+ eyeobj.setLoc(loc, TRUE)
set_eyeobj_visible(TRUE)
/mob/living/silicon/ai/proc/set_eyeobj_visible(state = TRUE)
@@ -241,7 +219,7 @@
acceleration = !acceleration
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
-/mob/eye/ai_eye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)
+/mob/eye/camera/ai/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)
. = ..()
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && GLOB.cameranet.checkCameraVis(speaker))
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
diff --git a/code/modules/mob/living/silicon/ai/multicam.dm b/code/modules/mob/living/silicon/ai/multicam.dm
index 12ee1ce3b53..049243d90f1 100644
--- a/code/modules/mob/living/silicon/ai/multicam.dm
+++ b/code/modules/mob/living/silicon/ai/multicam.dm
@@ -4,11 +4,11 @@
var/mob/living/silicon/ai/ai
var/mutable_appearance/highlighted_background
var/highlighted = FALSE
- var/mob/eye/ai_eye/pic_in_pic/aiEye
+ var/mob/eye/camera/ai/pic_in_pic/aiEye
/atom/movable/screen/movable/pic_in_pic/ai/Initialize(mapload, datum/hud/hud_owner)
. = ..()
- aiEye = new /mob/eye/ai_eye/pic_in_pic()
+ aiEye = new /mob/eye/camera/ai/pic_in_pic()
aiEye.screen = src
/atom/movable/screen/movable/pic_in_pic/ai/Destroy()
@@ -126,37 +126,38 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
//Dummy camera eyes
-/mob/eye/ai_eye/pic_in_pic
+/mob/eye/camera/ai/pic_in_pic
name = "Secondary AI Eye"
+ icon_state = "ai_pip_camera"
invisibility = INVISIBILITY_OBSERVER
mouse_opacity = MOUSE_OPACITY_ICON
- icon_state = "ai_pip_camera"
+ ai_detector_color = COLOR_ORANGE
+
var/atom/movable/screen/movable/pic_in_pic/ai/screen
var/list/cameras_telegraphed = list()
var/telegraph_cameras = TRUE
var/telegraph_range = 7
- ai_detector_color = COLOR_ORANGE
-/mob/eye/ai_eye/pic_in_pic/GetViewerClient()
+/mob/eye/camera/ai/pic_in_pic/GetViewerClient()
if(screen?.ai)
return screen.ai.client
-/mob/eye/ai_eye/pic_in_pic/setLoc(turf/destination, force_update = FALSE)
- if (destination)
- abstract_move(destination)
- else
- moveToNullspace()
+/mob/eye/camera/ai/pic_in_pic/update_visibility()
if(screen?.ai)
screen.ai.camera_visibility(src)
else
- GLOB.cameranet.visibility(src)
+ ..()
+
+/mob/eye/camera/ai/pic_in_pic/setLoc(turf/destination, force_update = FALSE)
+ . = ..()
update_camera_telegraphing()
update_ai_detect_hud()
-/mob/eye/ai_eye/pic_in_pic/get_visible_turfs()
+/mob/eye/camera/ai/pic_in_pic/get_visible_turfs()
+ SHOULD_CALL_PARENT(FALSE) //we do our own thing here
return screen ? screen.get_visible_turfs() : list()
-/mob/eye/ai_eye/pic_in_pic/proc/update_camera_telegraphing()
+/mob/eye/camera/ai/pic_in_pic/proc/update_camera_telegraphing()
if(!telegraph_cameras)
return
var/list/obj/machinery/camera/add = list()
@@ -185,7 +186,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
C.in_use_lights++
C.update_appearance()
-/mob/eye/ai_eye/pic_in_pic/proc/disable_camera_telegraphing()
+/mob/eye/camera/ai/pic_in_pic/proc/disable_camera_telegraphing()
telegraph_cameras = FALSE
for (var/obj/machinery/camera/C as anything in cameras_telegraphed)
if(QDELETED(C))
@@ -194,7 +195,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
C.update_appearance()
cameras_telegraphed.Cut()
-/mob/eye/ai_eye/pic_in_pic/Destroy()
+/mob/eye/camera/ai/pic_in_pic/Destroy()
disable_camera_telegraphing()
return ..()
diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm
index fc6d236f96a..66cbd4b93de 100644
--- a/code/modules/research/xenobiology/xenobio_camera.dm
+++ b/code/modules/research/xenobiology/xenobio_camera.dm
@@ -1,22 +1,20 @@
//Xenobio control console
-/mob/eye/ai_eye/remote/xenobio
- visible_icon = TRUE
- icon = 'icons/mob/eyemob.dmi'
- icon_state = "generic_camera"
+/mob/eye/camera/remote/xenobio
+ visible_to_user = TRUE
var/allowed_area = null
-/mob/eye/ai_eye/remote/xenobio/Initialize(mapload)
+/mob/eye/camera/remote/xenobio/Initialize(mapload)
var/area/our_area = get_area(loc)
allowed_area = our_area.name
. = ..()
-/mob/eye/ai_eye/remote/xenobio/setLoc(turf/destination, force_update = FALSE)
+/mob/eye/camera/remote/xenobio/setLoc(turf/destination, force_update = FALSE)
var/area/new_area = get_area(destination)
if(new_area && new_area.name == allowed_area || new_area && (new_area.area_flags & XENOBIOLOGY_COMPATIBLE))
return ..()
-/mob/eye/ai_eye/remote/xenobio/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider)
+/mob/eye/camera/remote/xenobio/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider)
. = ..()
if(!.)
return
@@ -84,11 +82,9 @@
stored_slimes -= gone
/obj/machinery/computer/camera_advanced/xenobio/CreateEye()
- eyeobj = new /mob/eye/ai_eye/remote/xenobio(get_turf(src))
- eyeobj.origin = src
- eyeobj.visible_icon = TRUE
- eyeobj.icon = 'icons/mob/eyemob.dmi'
- eyeobj.icon_state = "generic_camera"
+ eyeobj = new /mob/eye/camera/remote/xenobio(get_turf(src), src)
+
+ return TRUE
/obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/user)
. = ..()
@@ -151,7 +147,7 @@ Boilerplate check for a valid area to perform a camera action in.
Checks if the AI eye is on a valid turf and then checks if the target turf is xenobiology compatible
Due to keyboard shortcuts, the second one is not necessarily the remote eye's location.
*/
-/obj/machinery/computer/camera_advanced/xenobio/proc/validate_area(mob/living/user, mob/eye/ai_eye/remote/xenobio/remote_eye, turf/open/target_turf)
+/obj/machinery/computer/camera_advanced/xenobio/proc/validate_area(mob/living/user, mob/eye/camera/remote/xenobio/remote_eye, turf/open/target_turf)
if(!GLOB.cameranet.checkTurfVis(remote_eye.loc))
to_chat(user, span_warning("Target is not near a camera. Cannot proceed."))
return FALSE
@@ -228,7 +224,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!target || !isliving(owner))
return
var/mob/living/owner_mob = owner
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control
+ var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target
if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc))
@@ -245,7 +241,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!target || !isliving(owner))
return
var/mob/living/owner_mob = owner
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control
+ var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target
if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc))
@@ -264,7 +260,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!target || !isliving(owner))
return
var/mob/living/living_owner = owner
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = living_owner.remote_control
+ var/mob/eye/camera/remote/xenobio/remote_eye = living_owner.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target
if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc))
@@ -282,7 +278,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!target || !isliving(owner))
return
var/mob/living/owner_mob = owner
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control
+ var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target
var/obj/machinery/monkey_recycler/recycler = xeno_console.connected_recycler
@@ -305,7 +301,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!target || !isliving(owner))
return
var/mob/living/owner_mob = owner
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control
+ var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target
if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc))
@@ -324,7 +320,7 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
return
var/mob/living/owner_mob = owner
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control
+ var/mob/eye/camera/remote/xenobio/remote_eye = owner_mob.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target
if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc))
@@ -375,8 +371,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!isslime(target_slime))
return
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = user.remote_control
- var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin
+ var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control
+ var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve()
if(!xeno_console.validate_area(user, remote_eye, target_slime.loc))
return
@@ -391,8 +387,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickShift(mob/living/user, mob/living/basic/slime/target_slime)
SIGNAL_HANDLER
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = user.remote_control
- var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin
+ var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control
+ var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve()
if(!xeno_console.validate_area(user, remote_eye, target_slime.loc))
return
@@ -404,8 +400,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
SIGNAL_HANDLER
var/mob/living/user_mob = user
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = user_mob.remote_control
- var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin
+ var/mob/eye/camera/remote/xenobio/remote_eye = user_mob.remote_control
+ var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve()
if(!xeno_console.validate_area(user, remote_eye, target_turf))
return
@@ -430,8 +426,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
return
var/cleanup = FALSE
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = user.remote_control
- var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin
+ var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control
+ var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve()
if(!xeno_console.validate_area(user, remote_eye, target_turf))
return
@@ -449,8 +445,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!ismonkey(target_mob))
return
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = user.remote_control
- var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin
+ var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control
+ var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve()
if(!xeno_console.connected_recycler)
to_chat(user, span_warning("There is no connected monkey recycler. Use a multitool to link one."))
@@ -466,8 +462,8 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo
if(!isslime(target_slime))
return
- var/mob/eye/ai_eye/remote/xenobio/remote_eye = user.remote_control
- var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin
+ var/mob/eye/camera/remote/xenobio/remote_eye = user.remote_control
+ var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin_ref.resolve()
if(!xeno_console.validate_area(user, remote_eye, target_slime.loc))
return
diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm
index fbe528b8703..7c588e06dc0 100644
--- a/code/modules/shuttle/navigation_computer.dm
+++ b/code/modules/shuttle/navigation_computer.dm
@@ -107,8 +107,8 @@
shuttle_port = null
return
- eyeobj = new /mob/eye/ai_eye/remote/shuttle_docker(null, src)
- var/mob/eye/ai_eye/remote/shuttle_docker/the_eye = eyeobj
+ eyeobj = new /mob/eye/camera/remote/shuttle_docker(null, src)
+ var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj
the_eye.setDir(shuttle_port.dir)
var/turf/origin = locate(shuttle_port.x + x_offset, shuttle_port.y + y_offset, shuttle_port.z)
for(var/area/shuttle_area as anything in shuttle_port.shuttle_areas)
@@ -125,10 +125,12 @@
I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
the_eye.placement_images[I] = list(x_off, y_off)
+ return TRUE
+
/obj/machinery/computer/camera_advanced/shuttle_docker/give_eye_control(mob/user)
..()
if(!QDELETED(user) && user.client)
- var/mob/eye/ai_eye/remote/shuttle_docker/the_eye = eyeobj
+ var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj
var/list/to_add = list()
to_add += the_eye.placement_images
to_add += the_eye.placed_images
@@ -141,7 +143,7 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/remove_eye_control(mob/living/user)
..()
if(!QDELETED(user) && user.client)
- var/mob/eye/ai_eye/remote/shuttle_docker/the_eye = eyeobj
+ var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj
var/list/to_remove = list()
to_remove += the_eye.placement_images
to_remove += the_eye.placed_images
@@ -155,7 +157,7 @@
if(designating_target_loc || !current_user)
return
- var/mob/eye/ai_eye/remote/shuttle_docker/the_eye = eyeobj
+ var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj
var/landing_clear = checkLandingSpot()
if(designate_time && (landing_clear != SHUTTLE_DOCKER_BLOCKED))
to_chat(current_user, span_warning("Targeting transit location, please wait [DisplayTimeText(designate_time)]..."))
@@ -223,7 +225,7 @@
return TRUE
/obj/machinery/computer/camera_advanced/shuttle_docker/proc/rotateLandingSpot()
- var/mob/eye/ai_eye/remote/shuttle_docker/the_eye = eyeobj
+ var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj
var/list/image_cache = the_eye.placement_images
the_eye.setDir(turn(the_eye.dir, -90))
for(var/i in 1 to image_cache.len)
@@ -239,7 +241,7 @@
checkLandingSpot()
/obj/machinery/computer/camera_advanced/shuttle_docker/proc/checkLandingSpot()
- var/mob/eye/ai_eye/remote/shuttle_docker/the_eye = eyeobj
+ var/mob/eye/camera/remote/shuttle_docker/the_eye = eyeobj
var/turf/eyeturf = get_turf(the_eye)
if(!eyeturf)
return SHUTTLE_DOCKER_BLOCKED
@@ -316,22 +318,17 @@
add_jumpable_port(dock.shuttle_id)
return TRUE
-/mob/eye/ai_eye/remote/shuttle_docker
- visible_icon = FALSE
- use_static = FALSE
+/mob/eye/camera/remote/shuttle_docker
+ use_visibility = FALSE
var/list/image/placement_images = list()
var/list/image/placed_images = list()
-/mob/eye/ai_eye/remote/shuttle_docker/Initialize(mapload, obj/machinery/computer/camera_advanced/origin)
- src.origin = origin
- return ..()
-
-/mob/eye/ai_eye/remote/shuttle_docker/setLoc(turf/destination, force_update = FALSE)
+/mob/eye/camera/remote/shuttle_docker/setLoc(turf/destination, force_update = FALSE)
. = ..()
- var/obj/machinery/computer/camera_advanced/shuttle_docker/console = origin
+ var/obj/machinery/computer/camera_advanced/shuttle_docker/console = origin_ref?.resolve()
console.checkLandingSpot()
-/mob/eye/ai_eye/remote/shuttle_docker/update_remote_sight(mob/living/user)
+/mob/eye/camera/remote/shuttle_docker/update_remote_sight(mob/living/user)
user.set_sight(BLIND|SEE_TURFS)
// Pale blue, should look nice I think
user.lighting_color_cutoffs = list(30, 40, 50)
@@ -346,8 +343,8 @@
/datum/action/innate/shuttledocker_rotate/Activate()
if(QDELETED(owner) || !isliving(owner))
return
- var/mob/eye/ai_eye/remote/remote_eye = owner.remote_control
- var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin
+ var/mob/eye/camera/remote/remote_eye = owner.remote_control
+ var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin_ref.resolve()
origin.rotateLandingSpot()
/datum/action/innate/shuttledocker_place
@@ -358,8 +355,8 @@
/datum/action/innate/shuttledocker_place/Activate()
if(QDELETED(owner) || !isliving(owner))
return
- var/mob/eye/ai_eye/remote/remote_eye = owner.remote_control
- var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin
+ var/mob/eye/camera/remote/remote_eye = owner.remote_control
+ var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin_ref.resolve()
origin.placeLandingSpot(owner)
/datum/action/innate/camera_jump/shuttle_docker
@@ -369,8 +366,8 @@
/datum/action/innate/camera_jump/shuttle_docker/Activate()
if(QDELETED(owner) || !isliving(owner))
return
- var/mob/eye/ai_eye/remote/remote_eye = owner.remote_control
- var/obj/machinery/computer/camera_advanced/shuttle_docker/console = remote_eye.origin
+ var/mob/eye/camera/remote/remote_eye = owner.remote_control
+ var/obj/machinery/computer/camera_advanced/shuttle_docker/console = remote_eye.origin_ref.resolve()
playsound(console, 'sound/machines/terminal/terminal_prompt_deny.ogg', 25, FALSE)
diff --git a/code/modules/unit_tests/mob_faction.dm b/code/modules/unit_tests/mob_faction.dm
index f5d64918038..be667de731d 100644
--- a/code/modules/unit_tests/mob_faction.dm
+++ b/code/modules/unit_tests/mob_faction.dm
@@ -10,8 +10,8 @@
)
ignored += typesof(/mob/eye/imaginary_friend)
ignored += typesof(/mob/living/silicon/robot/model)
- ignored += typesof(/mob/eye/ai_eye/remote/base_construction)
- ignored += typesof(/mob/eye/ai_eye/remote/shuttle_docker)
+ ignored += typesof(/mob/eye/camera/remote/base_construction)
+ ignored += typesof(/mob/eye/camera/remote/shuttle_docker)
for (var/mob_type in typesof(/mob) - ignored)
var/mob/mob_instance = allocate(mob_type)
if(!islist(mob_instance.faction))
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index ce59bd3d61d..f5dee3e11ee 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -289,9 +289,9 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
//No heart to give
returnable_list += typesof(/obj/structure/ethereal_crystal)
//No linked console
- returnable_list += typesof(/mob/eye/ai_eye/remote/base_construction)
+ returnable_list += typesof(/mob/eye/camera/remote/base_construction)
//See above
- returnable_list += typesof(/mob/eye/ai_eye/remote/shuttle_docker)
+ returnable_list += typesof(/mob/eye/camera/remote/shuttle_docker)
//Hangs a ref post invoke async, which we don't support. Could put a qdeleted check but it feels hacky
returnable_list += typesof(/obj/effect/anomaly/grav/high)
//See above
diff --git a/code/modules/vehicles/mecha/mecha_ai_interaction.dm b/code/modules/vehicles/mecha/mecha_ai_interaction.dm
index 6dc1e2307dc..168b2e0ea02 100644
--- a/code/modules/vehicles/mecha/mecha_ai_interaction.dm
+++ b/code/modules/vehicles/mecha/mecha_ai_interaction.dm
@@ -99,7 +99,7 @@
mecha_flags |= SILICON_PILOT
moved_inside(AI)
AI.eyeobj?.forceMove(src)
- AI.eyeobj?.RegisterSignal(src, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/eye/ai_eye, update_visibility))
+ AI.eyeobj?.RegisterSignal(src, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/mob/eye/camera/ai, update_visibility))
AI.controlled_equipment = src
AI.remote_control = src
add_occupant(AI)
diff --git a/tgstation.dme b/tgstation.dme
index 51f540d2c85..c83d7d87c42 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -4799,6 +4799,8 @@
#include "code\modules\mob\dead\observer\observer_say.dm"
#include "code\modules\mob\dead\observer\orbit.dm"
#include "code\modules\mob\eye\eye.dm"
+#include "code\modules\mob\eye\camera\camera.dm"
+#include "code\modules\mob\eye\camera\remote.dm"
#include "code\modules\mob\living\blood.dm"
#include "code\modules\mob\living\damage_procs.dm"
#include "code\modules\mob\living\death.dm"