AI detection multitools can show camera static and AI eyes (#38255)

The old color-change functionality of the multitool remains unchanged. The hud is toggled with an action button, because multitools already have an attack_self() that does something. When toggled on, you get the hud as long as the multitool is in any slot on your body.

The only way I could find to reliably show exactly where an AI is looking was to place an image on every turf they are looking at. Because AI eyes move around a lot and I do not want them placing images all over the place when they do not have to, if there is nobody in the world currently using an ai detection multitool, they will not update the images until someone is.

Only AI eyes and multicamera zones currently appear. Advanced camera consoles do not appear on the hud, and no longer trigger the proximity color change. Also fixed multitools not appearing in-hands if their icon state changed.
This commit is contained in:
Cruix
2018-07-08 17:19:55 +12:00
committed by oranges
parent 60b5ce8fd3
commit 275aa2bfa2
19 changed files with 292 additions and 93 deletions
+17 -15
View File
@@ -20,8 +20,9 @@
#define DIAG_PATH_HUD "16"//Bot path indicators
#define GLAND_HUD "17"//Gland indicators for abductors
#define SENTIENT_DISEASE_HUD "18"
#define AI_DETECT_HUD "19"
//for antag huds. these are used at the /mob level
#define ANTAG_HUD "19"
#define ANTAG_HUD "20"
//by default everything in the hud_list of an atom is an image
//a value in hud_list with one of these will change that behavior
@@ -37,22 +38,23 @@
#define DATA_HUD_DIAGNOSTIC_ADVANCED 6
#define DATA_HUD_ABDUCTOR 7
#define DATA_HUD_SENTIENT_DISEASE 8
#define DATA_HUD_AI_DETECT 9
//antag HUD defines
#define ANTAG_HUD_CULT 9
#define ANTAG_HUD_REV 10
#define ANTAG_HUD_OPS 11
#define ANTAG_HUD_WIZ 12
#define ANTAG_HUD_SHADOW 13
#define ANTAG_HUD_TRAITOR 14
#define ANTAG_HUD_NINJA 15
#define ANTAG_HUD_CHANGELING 16
#define ANTAG_HUD_ABDUCTOR 17
#define ANTAG_HUD_DEVIL 18
#define ANTAG_HUD_SINTOUCHED 19
#define ANTAG_HUD_SOULLESS 20
#define ANTAG_HUD_CLOCKWORK 21
#define ANTAG_HUD_BROTHER 22
#define ANTAG_HUD_CULT 10
#define ANTAG_HUD_REV 11
#define ANTAG_HUD_OPS 12
#define ANTAG_HUD_WIZ 13
#define ANTAG_HUD_SHADOW 14
#define ANTAG_HUD_TRAITOR 15
#define ANTAG_HUD_NINJA 16
#define ANTAG_HUD_CHANGELING 17
#define ANTAG_HUD_ABDUCTOR 18
#define ANTAG_HUD_DEVIL 19
#define ANTAG_HUD_SINTOUCHED 20
#define ANTAG_HUD_SOULLESS 21
#define ANTAG_HUD_CLOCKWORK 22
#define ANTAG_HUD_BROTHER 23
// Notification action types
#define NOTIFY_JUMP "jump"
+15 -13
View File
@@ -85,20 +85,22 @@
#define BYOND_LIGHTING_PLANE 17
#define BYOND_LIGHTING_LAYER 17
#define CAMERA_STATIC_PLANE 18
#define CAMERA_STATIC_LAYER 18
//HUD layer defines
#define FULLSCREEN_PLANE 18
#define FLASH_LAYER 18
#define FULLSCREEN_LAYER 18.1
#define UI_DAMAGE_LAYER 18.2
#define BLIND_LAYER 18.3
#define CRIT_LAYER 18.4
#define CURSE_LAYER 18.5
#define FULLSCREEN_PLANE 19
#define FLASH_LAYER 19
#define FULLSCREEN_LAYER 19.1
#define UI_DAMAGE_LAYER 19.2
#define BLIND_LAYER 19.3
#define CRIT_LAYER 19.4
#define CURSE_LAYER 19.5
#define HUD_PLANE 19
#define HUD_LAYER 19
#define ABOVE_HUD_PLANE 20
#define ABOVE_HUD_LAYER 20
#define HUD_PLANE 20
#define HUD_LAYER 20
#define ABOVE_HUD_PLANE 21
#define ABOVE_HUD_LAYER 21
#define SPLASHSCREEN_LAYER 21
#define SPLASHSCREEN_PLANE 21
#define SPLASHSCREEN_LAYER 22
#define SPLASHSCREEN_PLANE 22
+5
View File
@@ -28,3 +28,8 @@
#define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses
#define VISOR_DARKNESSVIEW (1<<3)
#define VISOR_INVISVIEW (1<<4)
//for whether AI eyes see static, and whether it is mouse-opaque or not
#define USE_STATIC_NONE 0
#define USE_STATIC_TRANSPARENT 1
#define USE_STATIC_OPAQUE 2
+1
View File
@@ -26,6 +26,7 @@ GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list(),list())) // One for e
GLOBAL_LIST_EMPTY(spidermobs) //all sentient spider mobs
GLOBAL_LIST_EMPTY(bots_list)
GLOBAL_LIST_EMPTY(living_cameras)
GLOBAL_LIST_EMPTY(aiEyes)
GLOBAL_LIST_EMPTY(language_datum_instances)
GLOBAL_LIST_EMPTY(all_languages)
+8 -6
View File
@@ -124,14 +124,16 @@
vis_contents -= viewing_turfs
if(!width || !height)
return
var/turf/T = get_turf(center)
if(!T)
return
var/turf/lowerleft = locate(max(1, T.x - round(width/2)), max(1, T.y - round(height/2)), T.z)
var/turf/upperright = locate(min(world.maxx, lowerleft.x + width - 1), min(world.maxy, lowerleft.y + height - 1), lowerleft.z)
viewing_turfs = block(lowerleft, upperright)
viewing_turfs = get_visible_turfs()
vis_contents += viewing_turfs
/obj/screen/movable/pic_in_pic/proc/get_visible_turfs()
var/turf/T = get_turf(center)
if(!T)
return list()
var/turf/lowerleft = locate(max(1, T.x - round(width/2)), max(1, T.y - round(height/2)), T.z)
var/turf/upperright = locate(min(world.maxx, lowerleft.x + width - 1), min(world.maxy, lowerleft.y + height - 1), lowerleft.z)
return block(lowerleft, upperright)
/obj/screen/movable/pic_in_pic/proc/show_to(client/C)
if(C)
+6
View File
@@ -52,3 +52,9 @@
/obj/screen/plane_master/lighting/backdrop(mob/mymob)
mymob.overlay_fullscreen("lighting_backdrop_lit", /obj/screen/fullscreen/lighting_backdrop/lit)
mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit)
/obj/screen/plane_master/camera_static
name = "camera static plane master"
plane = CAMERA_STATIC_PLANE
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY
-1
View File
@@ -60,7 +60,6 @@
user.remote_control = null
if(!QDELETED(eye))
eye.RemoveImages()
QDEL_NULL(eye)
if(connected_holopad && !QDELETED(hologram))
+3 -1
View File
@@ -12,6 +12,7 @@ GLOBAL_LIST_INIT(huds, list(
DATA_HUD_DIAGNOSTIC_ADVANCED = new/datum/atom_hud/data/diagnostic/advanced(),
DATA_HUD_ABDUCTOR = new/datum/atom_hud/abductor(),
DATA_HUD_SENTIENT_DISEASE = new/datum/atom_hud/sentient_disease(),
DATA_HUD_AI_DETECT = new/datum/atom_hud/ai_detector(),
ANTAG_HUD_CULT = new/datum/atom_hud/antag(),
ANTAG_HUD_REV = new/datum/atom_hud/antag(),
ANTAG_HUD_OPS = new/datum/atom_hud/antag(),
@@ -100,7 +101,8 @@ GLOBAL_LIST_INIT(huds, list(
return FALSE
hudatoms |= A
for(var/mob/M in hudusers)
add_to_single_hud(M, A)
if(!queued_to_see[M])
add_to_single_hud(M, A)
return TRUE
/datum/atom_hud/proc/add_to_single_hud(mob/M, atom/A) //unsafe, no sanity apart from client
+10
View File
@@ -66,6 +66,16 @@
/datum/atom_hud/sentient_disease
hud_icons = list(SENTIENT_DISEASE_HUD)
/datum/atom_hud/ai_detector
hud_icons = list(AI_DETECT_HUD)
/datum/atom_hud/ai_detector/add_hud_to(mob/M)
..()
if(M && (hudusers.len == 1))
for(var/V in GLOB.aiEyes)
var/mob/camera/aiEye/E = V
E.update_ai_detect_hud()
/* MED/SEC/DIAG HUD HOOKS */
/*
@@ -59,7 +59,8 @@
C.remove(eyeobj)
if(user.client)
user.reset_perspective(null)
eyeobj.RemoveImages()
if(eyeobj.visible_icon && user.client)
user.client.images -= eyeobj.user_image
eyeobj.eye_user = null
user.remote_control = null
@@ -108,7 +109,7 @@
if(!eyeobj.eye_initialized)
var/camera_location
var/turf/myturf = get_turf(src)
if(eyeobj.use_static)
if(eyeobj.use_static != USE_STATIC_NONE)
if((!z_lock.len || (myturf.z in z_lock)) && GLOB.cameranet.checkTurfVis(myturf))
camera_location = myturf
else
@@ -151,6 +152,7 @@
/mob/camera/aiEye/remote
name = "Inactive Camera Eye"
ai_detector_visible = FALSE
var/sprint = 10
var/cooldown = 0
var/acceleration = 1
@@ -166,14 +168,9 @@
user.see_in_dark = 2
return 1
/mob/camera/aiEye/remote/RemoveImages()
..()
if(visible_icon)
var/client/C = GetViewerClient()
if(C)
C.images -= user_image
/mob/camera/aiEye/remote/Destroy()
if(origin && eye_user)
origin.remove_eye_control(eye_user)
origin = null
. = ..()
eye_user = null
@@ -185,15 +182,14 @@
/mob/camera/aiEye/remote/setLoc(T)
if(eye_user)
if(!isturf(eye_user.loc))
return
T = get_turf(T)
if (T)
forceMove(T)
else
moveToNullspace()
if(use_static)
GLOB.cameranet.visibility(src, GetViewerClient())
update_ai_detect_hud()
if(use_static != USE_STATIC_NONE)
GLOB.cameranet.visibility(src, GetViewerClient(), null, use_static)
if(visible_icon)
if(eye_user.client)
eye_user.client.images -= user_image
+84 -6
View File
@@ -15,6 +15,7 @@
desc = "Used for pulsing wires to test which to cut. Not recommended by doctors."
icon = 'icons/obj/device.dmi'
icon_state = "multitool"
item_state = "multitool"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
force = 5
@@ -40,8 +41,6 @@
if(selected_io)
selected_io = null
to_chat(user, "<span class='notice'>You clear the wired connection from the multitool.</span>")
else
..()
update_icon()
/obj/item/multitool/update_icon()
@@ -98,30 +97,89 @@
// Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby.
/obj/item/multitool/ai_detect
var/track_cooldown = 0
var/track_delay = 10 //How often it checks for proximity
var/detect_state = PROXIMITY_NONE
var/rangealert = 8 //Glows red when inside
var/rangewarning = 20 //Glows yellow when inside
var/hud_type = DATA_HUD_AI_DETECT
var/hud_on = FALSE
var/mob/camera/aiEye/remote/ai_detector/eye
var/datum/action/item_action/toggle_multitool/toggle_action
/obj/item/multitool/ai_detect/New()
..()
/obj/item/multitool/ai_detect/Initialize()
. = ..()
START_PROCESSING(SSobj, src)
eye = new /mob/camera/aiEye/remote/ai_detector()
toggle_action = new /datum/action/item_action/toggle_multitool(src)
/obj/item/multitool/ai_detect/Destroy()
STOP_PROCESSING(SSobj, src)
if(hud_on && ismob(loc))
remove_hud(loc)
QDEL_NULL(toggle_action)
QDEL_NULL(eye)
return ..()
/obj/item/multitool/ai_detect/ui_action_click()
return
/obj/item/multitool/ai_detect/update_icon()
if(selected_io)
icon_state = "multitool_red"
else
icon_state = "[initial(icon_state)][detect_state]"
/obj/item/multitool/ai_detect/equipped(mob/living/carbon/human/user, slot)
..()
if(hud_on)
show_hud(user)
/obj/item/multitool/ai_detect/dropped(mob/living/carbon/human/user)
..()
if(hud_on)
remove_hud(user)
/obj/item/multitool/ai_detect/process()
if(track_cooldown > world.time)
return
detect_state = PROXIMITY_NONE
if(eye.eye_user)
eye.setLoc(get_turf(src))
multitool_detect()
icon_state = "[initial(icon_state)][detect_state]"
update_icon()
track_cooldown = world.time + track_delay
/obj/item/multitool/ai_detect/proc/toggle_hud(mob/user)
hud_on = !hud_on
if(user)
to_chat(user, "<span class='notice'>You toggle the ai detection HUD on [src] [hud_on ? "on" : "off"].</span>")
if(hud_on)
show_hud(user)
else
remove_hud(user)
/obj/item/multitool/ai_detect/proc/show_hud(mob/user)
if(user && hud_type)
var/obj/screen/plane_master/camera_static/PM = user.hud_used.plane_masters["[CAMERA_STATIC_PLANE]"]
PM.alpha = 150
var/datum/atom_hud/H = GLOB.huds[hud_type]
if(!H.hudusers[user])
H.add_hud_to(user)
eye.eye_user = user
eye.setLoc(get_turf(src))
/obj/item/multitool/ai_detect/proc/remove_hud(mob/user)
if(user && hud_type)
var/obj/screen/plane_master/camera_static/PM = user.hud_used.plane_masters["[CAMERA_STATIC_PLANE]"]
PM.alpha = 255
var/datum/atom_hud/H = GLOB.huds[hud_type]
H.remove_hud_from(user)
if(eye)
eye.setLoc(null)
eye.eye_user = null
/obj/item/multitool/ai_detect/proc/multitool_detect()
var/turf/our_turf = get_turf(src)
for(var/mob/living/silicon/ai/AI in GLOB.ai_list)
@@ -134,6 +192,8 @@
var/datum/camerachunk/chunk = GLOB.cameranet.chunkGenerated(our_turf.x, our_turf.y, our_turf.z)
if(chunk && chunk.seenby.len)
for(var/mob/camera/aiEye/A in chunk.seenby)
if(!A.ai_detector_visible)
continue
var/turf/detect_turf = get_turf(A)
if(get_dist(our_turf, detect_turf) < rangealert)
detect_state = PROXIMITY_ON_SCREEN
@@ -142,6 +202,24 @@
detect_state = PROXIMITY_NEAR
break
/mob/camera/aiEye/remote/ai_detector
name = "AI detector eye"
ai_detector_visible = FALSE
use_static = USE_STATIC_TRANSPARENT
visible_icon = FALSE
/datum/action/item_action/toggle_multitool
name = "Toggle AI detector HUD"
check_flags = NONE
/datum/action/item_action/toggle_multitool/Trigger()
if(!..())
return 0
if(target)
var/obj/item/multitool/ai_detect/M = target
M.toggle_hud(owner)
return 1
/obj/item/multitool/cyborg
name = "multitool"
desc = "Optimised and stripped-down version of a regular multitool."
+2 -2
View File
@@ -331,9 +331,9 @@
var/datum/camerachunk/C = GLOB.cameranet.chunkGenerated(x, y, z)
if(C)
if(C.obscuredTurfs[src])
vis_contents += GLOB.cameranet.vis_contents
vis_contents += GLOB.cameranet.vis_contents_objects
else
vis_contents -= GLOB.cameranet.vis_contents
vis_contents -= GLOB.cameranet.vis_contents_objects
/turf/proc/burn_tile()
+1 -1
View File
@@ -1003,7 +1003,7 @@
target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct
/mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye)
GLOB.cameranet.visibility(moved_eye, client, all_eyes)
GLOB.cameranet.visibility(moved_eye, client, all_eyes, USE_STATIC_OPAQUE)
/mob/living/silicon/ai/forceMove(atom/destination)
. = ..()
@@ -18,15 +18,24 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
// The object used for the clickable stat() button.
var/obj/effect/statclick/statclick
// The object used in vis_contents of obscured turfs
var/vis_contents
// The objects used in vis_contents of obscured turfs
var/list/vis_contents_objects
var/obj/effect/overlay/camera_static/vis_contents_opaque
var/obj/effect/overlay/camera_static/vis_contents_transparent
// The image given to the effect in vis_contents on AI clients
var/image/obscured
var/image/obscured_transparent
/datum/cameranet/New()
vis_contents = new /obj/effect/overlay/camera_static()
obscured = new('icons/effects/cameravis.dmi', vis_contents, null, BYOND_LIGHTING_LAYER + 0.1)
obscured.plane = BYOND_LIGHTING_PLANE + 1
vis_contents_opaque = new /obj/effect/overlay/camera_static()
vis_contents_transparent = new /obj/effect/overlay/camera_static/transparent()
vis_contents_objects = list(vis_contents_opaque, vis_contents_transparent)
obscured = new('icons/effects/cameravis.dmi', vis_contents_opaque, null, CAMERA_STATIC_LAYER)
obscured.plane = CAMERA_STATIC_PLANE
obscured_transparent = new('icons/effects/cameravis.dmi', vis_contents_transparent, null, CAMERA_STATIC_LAYER)
obscured_transparent.plane = CAMERA_STATIC_PLANE
// Checks if a chunk has been Generated in x, y, z.
/datum/cameranet/proc/chunkGenerated(x, y, z)
@@ -46,7 +55,7 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set.
/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes)
/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes, use_static = USE_STATIC_OPAQUE)
if(!islist(moved_eyes))
moved_eyes = moved_eyes ? list(moved_eyes) : list()
if(islist(other_eyes))
@@ -54,19 +63,28 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
else
other_eyes = list()
if(C)
switch(use_static)
if(USE_STATIC_TRANSPARENT)
C.images += obscured_transparent
if(USE_STATIC_OPAQUE)
C.images += obscured
for(var/V in moved_eyes)
var/mob/camera/aiEye/eye = V
var/static_range = eye.static_visibility_range
var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1)
var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1)
var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1)
var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1)
var/list/visibleChunks = list()
if(eye.loc)
// 0xf = 15
var/static_range = eye.static_visibility_range
var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1)
var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1)
var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1)
var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1)
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
visibleChunks |= getCameraChunk(x, y, eye.z)
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
visibleChunks |= getCameraChunk(x, y, eye.z)
var/list/remove = eye.visibleCameraChunks - visibleChunks
var/list/add = visibleChunks - eye.visibleCameraChunks
@@ -79,9 +97,6 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
var/datum/camerachunk/c = chunk
c.add(eye)
if(C)
C.images += obscured
// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
/datum/cameranet/proc/updateVisibility(atom/A, opacity_check = 1)
@@ -173,5 +188,8 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
mouse_opacity = MOUSE_OPACITY_ICON
invisibility = INVISIBILITY_ABSTRACT
layer = BYOND_LIGHTING_LAYER + 0.1
plane = BYOND_LIGHTING_PLANE + 1
layer = CAMERA_STATIC_LAYER
plane = CAMERA_STATIC_PLANE
/obj/effect/overlay/camera_static/transparent
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
@@ -29,6 +29,14 @@
/datum/camerachunk/proc/remove(mob/camera/aiEye/eye)
eye.visibleCameraChunks -= src
seenby -= eye
if(!eye.visibleCameraChunks.len)
var/client/client = eye.GetViewerClient()
if(client)
switch(eye.use_static)
if(USE_STATIC_TRANSPARENT)
client.images -= GLOB.cameranet.obscured_transparent
if(USE_STATIC_OPAQUE)
client.images -= GLOB.cameranet.obscured
// Called when a chunk has changed. I.E: A wall was deleted.
@@ -81,12 +89,12 @@
for(var/turf in visAdded)
var/turf/t = turf
t.vis_contents -= GLOB.cameranet.vis_contents
t.vis_contents -= GLOB.cameranet.vis_contents_objects
for(var/turf in visRemoved)
var/turf/t = turf
if(obscuredTurfs[t] && !istype(t, /turf/open/ai_visible))
t.vis_contents += GLOB.cameranet.vis_contents
t.vis_contents += GLOB.cameranet.vis_contents_objects
changed = 0
@@ -128,7 +136,7 @@
for(var/turf in obscuredTurfs)
var/turf/t = turf
t.vis_contents += GLOB.cameranet.vis_contents
t.vis_contents += GLOB.cameranet.vis_contents_objects
#undef UPDATE_BUFFER
#undef CHUNK_SIZE
@@ -7,28 +7,81 @@
name = "Inactive AI Eye"
invisibility = INVISIBILITY_MAXIMUM
hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
var/list/visibleCameraChunks = list()
var/mob/living/silicon/ai/ai = null
var/relay_speech = FALSE
var/use_static = TRUE
var/use_static = USE_STATIC_OPAQUE
var/static_visibility_range = 16
var/ai_detector_visible = TRUE
var/ai_detector_color = COLOR_RED
/mob/camera/aiEye/Initialize()
. = ..()
GLOB.aiEyes += src
update_ai_detect_hud()
setLoc(loc, TRUE)
/mob/camera/aiEye/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)
hud.remove_from_hud(src)
QDEL_LIST(old_images)
return
if(!hud.hudusers.len)
//no one is watching, do not bother updating anything
return
hud.remove_from_hud(src)
var/static/list/vis_contents_objects = list()
var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_objects[ai_detector_color]
if(!hud_obj)
hud_obj = new /obj/effect/overlay/ai_detect_hud()
hud_obj.color = ai_detector_color
vis_contents_objects[ai_detector_color] = hud_obj
var/list/new_images = list()
var/list/turfs = get_visible_turfs()
for(var/T in turfs)
var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T)
I.loc = T
I.vis_contents += hud_obj
new_images += I
for(var/i in (new_images.len + 1) to old_images.len)
qdel(old_images[i])
hud_list[AI_DETECT_HUD] = new_images
hud.add_to_hud(src)
/mob/camera/aiEye/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)
// Use this when setting the aiEye's location.
// It will also stream the chunk that the new loc is in.
/mob/camera/aiEye/proc/setLoc(T)
/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE)
if(ai)
if(!isturf(ai.loc))
return
T = get_turf(T)
if(!force_update && (T == get_turf(src)) )
return //we are already here!
if (T)
forceMove(T)
else
moveToNullspace() // ????
if(use_static)
moveToNullspace()
if(use_static != USE_STATIC_NONE)
ai.camera_visibility(src)
if(ai.client && !ai.multicam_on)
ai.client.eye = src
update_ai_detect_hud()
update_parallax_contents()
//Holopad
if(istype(ai.current, /obj/machinery/holopad))
@@ -47,11 +100,6 @@
return ai.client
return null
/mob/camera/aiEye/proc/RemoveImages()
var/client/C = GetViewerClient()
if(C && use_static)
C.images -= GLOB.cameranet.obscured
/mob/camera/aiEye/Destroy()
if(ai)
ai.all_eyes -= src
@@ -59,6 +107,12 @@
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_from_hud(src)
var/list/L = hud_list[AI_DETECT_HUD]
QDEL_LIST(L)
return ..()
/atom/proc/move_camera_by_click()
@@ -131,3 +185,12 @@
/mob/camera/aiEye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
/obj/effect/overlay/ai_detect_hud
name = ""
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
icon = 'icons/effects/alphacolors.dmi'
icon_state = ""
alpha = 100
layer = ABOVE_ALL_MOB_LAYER
plane = GAME_PLANE
@@ -124,6 +124,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
var/list/cameras_telegraphed = list()
var/telegraph_cameras = TRUE
var/telegraph_range = 7
ai_detector_color = COLOR_ORANGE
/mob/camera/aiEye/pic_in_pic/GetViewerClient()
if(screen && screen.ai)
@@ -139,6 +140,10 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
else
GLOB.cameranet.visibility(src)
update_camera_telegraphing()
update_ai_detect_hud()
/mob/camera/aiEye/pic_in_pic/get_visible_turfs()
return screen ? screen.get_visible_turfs() : list()
/mob/camera/aiEye/pic_in_pic/proc/update_camera_telegraphing()
if(!telegraph_cameras)
+1 -1
View File
@@ -249,7 +249,7 @@
/mob/camera/aiEye/remote/shuttle_docker
visible_icon = FALSE
use_static = FALSE
use_static = USE_STATIC_NONE
var/list/placement_images = list()
var/list/placed_images = list()
+4 -2
View File
@@ -1002,8 +1002,10 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/device_tools/ai_detector
name = "Artificial Intelligence Detector"
desc = "A functional multitool that turns red when it detects an artificial intelligence watching it or its \
holder. Knowing when an artificial intelligence is watching you is useful for knowing when to maintain cover."
desc = "A functional multitool that turns red when it detects an artificial intelligence watching it, and can be \
activated to display their exact viewing location and nearby security camera blind spots. Knowing when \
an artificial intelligence is watching you is useful for knowing when to maintain cover, and finding nearby \
blind spots can help you identify escape routes."
item = /obj/item/multitool/ai_detect
cost = 1