Add Picture-in-Picture Mode for AI's, Working on Resolving Runtime

WIP DESC
This commit is contained in:
Rykka
2020-05-10 12:31:49 -04:00
parent 6021131272
commit 3b3a7b960c
22 changed files with 1485 additions and 24749 deletions

View File

@@ -32,6 +32,14 @@
build_click(src, client.buildmode, params, A)
return
if(multicam_on)
var/turf/T = get_turf(A)
if(T)
for(var/obj/screen/movable/pic_in_pic/ai/P in T.vis_locs)
if(P.ai == src)
P.Click(params)
break
if(stat)
return

View File

@@ -75,6 +75,8 @@
#define ui_ai_pda_log "SOUTH:6,WEST+11:16"
#define ui_ai_take_picture "SOUTH:6,WEST+12:16"
#define ui_ai_view_images "SOUTH:6,WEST+13:16"
#define ui_ai_multicam "SOUTH+1:6,WEST+13"
#define ui_ai_add_multicam "SOUTH+1:6,WEST+14"
//Gun buttons
#define ui_gun1 "EAST-2:26,SOUTH+2:7"

View File

@@ -1,3 +1,23 @@
/obj/screen/ai/multicam
name = "Multicamera Mode"
icon_state = "multicam"
/obj/screen/ai/multicam/Click()
if(..())
return
var/mob/living/silicon/ai/AI = usr
AI.toggle_multicam()
/obj/screen/ai/add_multicam
name = "New Camera"
icon_state = "new_cam"
/obj/screen/ai/add_multicam/Click()
if(..())
return
var/mob/living/silicon/ai/AI = usr
AI.drop_new_multicam()
/datum/hud/proc/ai_hud()
adding = list()
other = list()
@@ -130,6 +150,16 @@
using.layer = SCREEN_LAYER
adding += using
//Multicamera mode
using = new /obj/screen/ai/multicam()
using.screen_loc = ui_ai_multicam
adding += using
//Add multicamera camera
using = new /obj/screen/ai/add_multicam()
using.screen_loc = ui_ai_add_multicam
adding += using
mymob.client.screen = list()
mymob.client.screen += adding + other
mymob.client.screen += mymob.client.void

View File

@@ -11,6 +11,8 @@
/obj/screen/movable
var/snap2grid = FALSE
var/moved = FALSE
var/x_off = -16
var/y_off = -16
//Snap Screen Object
//Tied to the grid, snaps to the nearest turf
@@ -40,8 +42,8 @@
screen_loc = "[screen_loc_X[1]],[screen_loc_Y[1]]"
else //Normalise Pixel Values (So the object drops at the center of the mouse, not 16 pixels off)
var/pix_X = text2num(screen_loc_X[2]) - 16
var/pix_Y = text2num(screen_loc_Y[2]) - 16
var/pix_X = text2num(screen_loc_X[2]) + x_off
var/pix_Y = text2num(screen_loc_Y[2]) + y_off
screen_loc = "[screen_loc_X[1]]:[pix_X],[screen_loc_Y[1]]:[pix_Y]"
/obj/screen/movable/proc/encode_screen_X(X)

View File

@@ -0,0 +1,145 @@
/obj/screen/movable/pic_in_pic
name = "Picture-in-picture"
screen_loc = "CENTER"
plane = PLANE_WORLD
var/atom/center
var/width = 0
var/height = 0
var/list/shown_to = list()
var/list/viewing_turfs = list()
var/obj/screen/component_button/button_x
var/obj/screen/component_button/button_expand
var/obj/screen/component_button/button_shrink
var/mutable_appearance/standard_background
var/const/max_dimensions = 10
/obj/screen/movable/pic_in_pic/Initialize()
. = ..()
make_backgrounds()
/obj/screen/movable/pic_in_pic/Destroy()
for(var/C in shown_to)
unshow_to(C)
QDEL_NULL(button_x)
QDEL_NULL(button_shrink)
QDEL_NULL(button_expand)
return ..()
/obj/screen/movable/pic_in_pic/component_click(obj/screen/component_button/component, params)
if(component == button_x)
qdel(src)
else if(component == button_expand)
set_view_size(width+1, height+1)
else if(component == button_shrink)
set_view_size(width-1, height-1)
/obj/screen/movable/pic_in_pic/proc/make_backgrounds()
standard_background = new /mutable_appearance()
standard_background.icon = 'icons/misc/pic_in_pic.dmi'
standard_background.icon_state = "background"
standard_background.layer = DISPOSAL_LAYER
standard_background.plane = PLATING_PLANE
/obj/screen/movable/pic_in_pic/proc/add_buttons()
var/static/mutable_appearance/move_tab
if(!move_tab)
move_tab = new /mutable_appearance()
//all these properties are always the same, and since adding something to the overlay
//list makes a copy, there is no reason to make a new one each call
move_tab.icon = 'icons/misc/pic_in_pic.dmi'
move_tab.icon_state = "move"
move_tab.plane = PLANE_PLAYER_HUD
var/matrix/M = matrix()
M.Translate(0, (height + 0.25) * world.icon_size)
move_tab.transform = M
overlays += move_tab
if(!button_x)
button_x = new /obj/screen/component_button(null, src)
var/mutable_appearance/MA = new /mutable_appearance()
MA.name = "close"
MA.icon = 'icons/misc/pic_in_pic.dmi'
MA.icon_state = "x"
MA.plane = PLANE_PLAYER_HUD
button_x.appearance = MA
M = matrix()
M.Translate((max(4, width) - 0.75) * world.icon_size, (height + 0.25) * world.icon_size)
button_x.transform = M
vis_contents += button_x
if(!button_expand)
button_expand = new /obj/screen/component_button(null, src)
var/mutable_appearance/MA = new /mutable_appearance()
MA.name = "expand"
MA.icon = 'icons/misc/pic_in_pic.dmi'
MA.icon_state = "expand"
MA.plane = PLANE_PLAYER_HUD
button_expand.appearance = MA
M = matrix()
M.Translate(world.icon_size, (height + 0.25) * world.icon_size)
button_expand.transform = M
vis_contents += button_expand
if(!button_shrink)
button_shrink = new /obj/screen/component_button(null, src)
var/mutable_appearance/MA = new /mutable_appearance()
MA.name = "shrink"
MA.icon = 'icons/misc/pic_in_pic.dmi'
MA.icon_state = "shrink"
MA.plane = PLANE_PLAYER_HUD
button_shrink.appearance = MA
M = matrix()
M.Translate(2 * world.icon_size, (height + 0.25) * world.icon_size)
button_shrink.transform = M
vis_contents += button_shrink
/obj/screen/movable/pic_in_pic/proc/add_background()
if((width > 0) && (height > 0))
var/matrix/M = matrix()
M.Scale(width + 0.5, height + 0.5)
M.Translate((width-1)/2 * world.icon_size, (height-1)/2 * world.icon_size)
standard_background.transform = M
overlays += standard_background
/obj/screen/movable/pic_in_pic/proc/set_view_size(width, height, do_refresh = TRUE)
width = CLAMP(width, 0, max_dimensions)
height = CLAMP(height, 0, max_dimensions)
src.width = width
src.height = height
y_off = -height * world.icon_size - 16
overlays.Cut()
add_background()
add_buttons()
if(do_refresh)
refresh_view()
/obj/screen/movable/pic_in_pic/proc/set_view_center(atom/target, do_refresh = TRUE)
center = target
if(do_refresh)
refresh_view()
/obj/screen/movable/pic_in_pic/proc/refresh_view()
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)
vis_contents += viewing_turfs
/obj/screen/movable/pic_in_pic/proc/show_to(client/C)
if(C)
shown_to[C] = 1
C.screen += src
/obj/screen/movable/pic_in_pic/proc/unshow_to(client/C)
if(C)
shown_to -= C
C.screen -= src

View File

@@ -20,6 +20,9 @@
master = null
return ..()
/obj/screen/proc/component_click(obj/screen/component_button/component, params)
return
/obj/screen/text
icon = null
icon_state = null
@@ -535,3 +538,16 @@
var/mob/living/carbon/C = hud.mymob
if(C.handcuffed)
overlays |= handcuff_overlay
// PIP stuff
/obj/screen/component_button
var/obj/screen/parent
/obj/screen/component_button/Initialize(mapload, obj/screen/new_parent)
. = ..()
parent = new_parent
/obj/screen/component_button/Click(params)
if(parent)
parent.component_click(src, params)

View File

@@ -29,6 +29,7 @@
var/short_range = 2
var/light_disabled = 0
var/in_use_lights = 0 // TO BE IMPLEMENTED - LIES.
var/alarm_on = 0
var/busy = 0

View File

@@ -11,16 +11,35 @@
..()
visualnet = cameranet
/mob/observer/eye/aiEye/Destroy()
if(owner)
var/mob/living/silicon/ai/ai = owner
ai.all_eyes -= src
owner = null
. = ..()
/mob/observer/eye/aiEye/setLoc(var/T, var/cancel_tracking = 1)
if(..())
if(owner)
T = get_turf(T)
loc = T
var/mob/living/silicon/ai/ai = owner
if(cancel_tracking)
ai.ai_cancel_tracking()
//Holopad
if(use_static)
ai.camera_visibility(src)
if(ai.client && !ai.multicam_on)
ai.client.eye = src
if(ai.master_multicam)
ai.master_multicam.refresh_view()
if(ai.holo)
if(ai.hologram_follow)
ai.holo.move_hologram(ai)
return 1
// AI MOVEMENT
@@ -46,6 +65,7 @@
if(!newloc)
newloc = src.loc
eyeobj = new /mob/observer/eye/aiEye(newloc)
all_eyes += eyeobj
eyeobj.owner = src
eyeobj.name = "[src.name] (AI Eye)" // Give it a name
if(client)
@@ -66,7 +86,7 @@
/atom/proc/move_camera_by_click()
if(istype(usr, /mob/living/silicon/ai))
var/mob/living/silicon/ai/AI = usr
if(AI.eyeobj && AI.client.eye == AI.eyeobj)
if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)))
var/turf/T = get_turf(src)
if(T)
AI.eyeobj.setLoc(T)

View File

@@ -25,12 +25,12 @@
// Add an eye to the chunk, then update if changed.
/datum/chunk/proc/add(mob/observer/eye/eye)
if(!eye.owner)
return
/datum/chunk/proc/add(mob/observer/eye/eye, add_images = TRUE)
if(add_images)
var/client/client = eye.GetViewerClient()
if(client)
client.images += obscured
eye.visibleChunks += src
if(eye.owner.client)
eye.owner.client.images += obscured
visible++
seenby += eye
if(changed && !updating)
@@ -38,12 +38,12 @@
// Remove an eye from the chunk, then update if changed.
/datum/chunk/proc/remove(mob/observer/eye/eye)
if(!eye.owner)
return
/datum/chunk/proc/remove(mob/observer/eye/eye, remove_images = TRUE)
if(remove_images)
var/client/client = eye.GetViewerClient()
if(client)
client.images -= obscured
eye.visibleChunks -= src
if(eye.owner.client)
eye.owner.client.images -= obscured
seenby -= eye
if(visible > 0)
visible--
@@ -92,10 +92,11 @@
obscured -= t.obfuscations[obfuscation.type]
for(var/eye in seenby)
var/mob/observer/eye/m = eye
if(!m || !m.owner)
if(!m)
continue
if(m.owner.client)
m.owner.client.images -= t.obfuscations[obfuscation.type]
var/client/client = m.GetViewerClient()
if(client)
client.images -= t.obfuscations[obfuscation.type]
for(var/turf in visRemoved)
var/turf/t = turf
@@ -109,11 +110,12 @@
obscured += t.obfuscations[obfuscation.type]
for(var/eye in seenby)
var/mob/observer/eye/m = eye
if(!m || !m.owner)
if(!m)
seenby -= m
continue
if(m.owner.client)
m.owner.client.images += t.obfuscations[obfuscation.type]
var/client/client = m.GetViewerClient()
if(client)
client.images += t.obfuscations[obfuscation.type]
/datum/chunk/proc/acquireVisibleTurfs(var/list/visible)

View File

@@ -23,6 +23,8 @@
var/ghostimage = null
var/datum/visualnet/visualnet
var/use_static = TRUE
var/static_visibility_range = 16
/mob/observer/eye/Destroy()
if(owner)
@@ -67,8 +69,8 @@
visualnet.updateVisibility(owner, 0)
owner.loc = loc
visualnet.updateVisibility(owner, 0)
visualnet.visibility(src)
if(use_static)
visualnet.visibility(src, owner.client)
return 1
return 0
@@ -86,6 +88,11 @@
return eyeobj.EyeMove(n, direct)
/mob/observer/eye/proc/GetViewerClient()
if(owner)
return owner.client
return null
/mob/observer/eye/EyeMove(n, direct)
var/initial = initial(sprint)
var/max_sprint = 50

View File

@@ -2,6 +2,8 @@
//
// The datum containing all the chunks.
#define CHUNK_SIZE 16
/datum/visualnet
// The chunks of the map, mapping the areas that an object can see.
var/list/chunks = list()
@@ -36,29 +38,63 @@
// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set.
/datum/visualnet/proc/visibility(mob/observer/eye/eye)
/datum/visualnet/proc/visibility(list/moved_eyes, client/C, list/other_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()
var/list/chunks_pre_seen = list()
var/list/chunks_post_seen = list()
for(var/V in moved_eyes)
var/mob/observer/eye/eye = V
if(C)
chunks_pre_seen |= eye.visibleChunks
// 0xf = 15
var/x1 = max(0, eye.x - 16) & ~0xf
var/y1 = max(0, eye.y - 16) & ~0xf
var/x2 = min(world.maxx, eye.x + 16) & ~0xf
var/y2 = min(world.maxy, eye.y + 16) & ~0xf
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()
for(var/x = x1; x <= x2; x += 16)
for(var/y = y1; y <= y2; y += 16)
visibleChunks += getChunk(x, y, eye.z)
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
visibleChunks |= getChunk(x, y, eye.z)
var/list/remove = eye.visibleChunks - visibleChunks
var/list/add = visibleChunks - eye.visibleChunks
for(var/chunk in remove)
var/datum/chunk/c = chunk
c.remove(eye)
c.remove(eye, FALSE)
for(var/chunk in add)
var/datum/chunk/c = chunk
c.add(eye)
c.add(eye, FALSE)
if(C)
chunks_post_seen |= eye.visibleChunks
if(C)
for(var/V in other_eyes)
var/mob/observer/eye/eye = V
chunks_post_seen |= eye.visibleChunks
var/list/remove = chunks_pre_seen - chunks_post_seen
var/list/add = chunks_post_seen - chunks_pre_seen
for(var/chunk in remove)
var/datum/chunk/c = chunk
C.images -= c.obscured
for(var/chunk in add)
var/datum/chunk/c = chunk
C.images += c.obscured
// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.

View File

@@ -86,6 +86,14 @@ var/list/ai_verbs_default = list(
var/custom_sprite = 0 // Whether the selected icon is custom
var/carded
// Multicam Vars
var/multicam_allowed = TRUE
var/multicam_on = FALSE
var/obj/screen/movable/pic_in_pic/ai/master_multicam
var/list/multicam_screens = list()
var/list/all_eyes = list()
var/max_multicams = 6
can_be_antagged = TRUE
/mob/living/silicon/ai/proc/add_ai_verbs()
@@ -478,12 +486,27 @@ var/list/ai_verbs_default = list(
return
/mob/living/silicon/ai/proc/camera_visibility(mob/observer/eye/aiEye/moved_eye)
cameranet.visibility(moved_eye, client, all_eyes)
/mob/living/silicon/ai/forceMove(atom/destination)
. = ..()
if(.)
end_multicam()
/mob/living/silicon/ai/reset_view(atom/A)
if(camera)
camera.set_light(0)
if(istype(A,/obj/machinery/camera))
camera = A
..()
if(A != GLOB.ai_camera_room_landmark)
end_multicam()
. = ..()
if(.)
if(!A && isturf(loc) && eyeobj)
end_multicam()
client.eye = eyeobj
client.perspective = MOB_PERSPECTIVE
if(istype(A,/obj/machinery/camera))
if(camera_light_on) A.set_light(AI_CAMERA_LUMINOSITY)
else A.set_light(0)

View File

@@ -94,6 +94,7 @@
spawn(20)
to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.")
end_multicam()
sleep(50)
if (loc.power_equip)
if (!istype(T, /turf/space))

View File

@@ -1,4 +1,4 @@
/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up <20>_<EFBFBD> ~Carn
/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up <20>_<EFBFBD> ~Carn
..()
for(var/obj/effect/rune/rune in rune_list)
client.images += rune.blood_image
@@ -6,5 +6,7 @@
for(var/obj/machinery/ai_status_display/O in machines) //change status
O.mode = 1
O.emotion = "Neutral"
if(multicam_on)
end_multicam()
src.view_core()
return

View File

@@ -0,0 +1,260 @@
//Picture in picture
/obj/screen/movable/pic_in_pic/ai
var/mob/living/silicon/ai/ai
var/mutable_appearance/highlighted_background
var/highlighted = FALSE
var/mob/observer/eye/aiEye/pic_in_pic/aiEye
/obj/screen/movable/pic_in_pic/ai/Initialize()
. = ..()
aiEye = new /mob/observer/eye/aiEye/pic_in_pic()
aiEye.screen = src
/obj/screen/movable/pic_in_pic/ai/Destroy()
set_ai(null)
QDEL_NULL(aiEye)
return ..()
/obj/screen/movable/pic_in_pic/ai/Click()
..()
if(ai)
ai.select_main_multicam_window(src)
/obj/screen/movable/pic_in_pic/ai/make_backgrounds()
..()
highlighted_background = new /mutable_appearance()
highlighted_background.icon = 'icons/misc/pic_in_pic.dmi'
highlighted_background.icon_state = "background_highlight"
highlighted_background.layer = DISPOSAL_LAYER
highlighted_background.plane = PLATING_PLANE
/obj/screen/movable/pic_in_pic/ai/add_background()
if((width > 0) && (height > 0))
var/matrix/M = matrix()
M.Scale(width + 0.5, height + 0.5)
M.Translate((width-1)/2 * world.icon_size, (height-1)/2 * world.icon_size)
highlighted_background.transform = M
standard_background.transform = M
overlays += highlighted ? highlighted_background : standard_background
/obj/screen/movable/pic_in_pic/ai/set_view_size(width, height, do_refresh = TRUE)
aiEye.static_visibility_range = (round(max(width, height) / 2) + 1)
if(ai)
ai.camera_visibility(aiEye)
..()
/obj/screen/movable/pic_in_pic/ai/set_view_center(atom/target, do_refresh = TRUE)
..()
aiEye.setLoc(get_turf(target))
/obj/screen/movable/pic_in_pic/ai/refresh_view()
..()
aiEye.setLoc(get_turf(center))
/obj/screen/movable/pic_in_pic/ai/proc/highlight()
if(highlighted)
return
highlighted = TRUE
overlays -= standard_background
overlays += highlighted_background
/obj/screen/movable/pic_in_pic/ai/proc/unhighlight()
if(!highlighted)
return
highlighted = FALSE
overlays -= highlighted_background
overlays += standard_background
/obj/screen/movable/pic_in_pic/ai/proc/set_ai(mob/living/silicon/ai/new_ai)
if(ai)
ai.multicam_screens -= src
ai.all_eyes -= aiEye
if(ai.master_multicam == src)
ai.master_multicam = null
if(ai.multicam_on)
unshow_to(ai.client)
ai = new_ai
if(new_ai)
new_ai.multicam_screens += src
ai.all_eyes += aiEye
if(new_ai.multicam_on)
show_to(new_ai.client)
//Turf, area, and landmark for the viewing room
/turf/unsimulated/ai_visible
name = ""
icon = 'icons/misc/pic_in_pic.dmi'
icon_state = "room_background"
flags = NOJAUNT
/turf/unsimulated/ai_visible/Initialize()
. = ..()
/area/ai_multicam_room
name = "ai_multicam_room"
icon_state = "ai_camera_room"
dynamic_lighting = FALSE
ambience = list()
GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
/obj/effect/landmark/ai_multicam_room
name = "ai camera room"
icon_state = "x"
/obj/effect/landmark/ai_multicam_room/Initialize()
. = ..()
qdel(GLOB.ai_camera_room_landmark)
GLOB.ai_camera_room_landmark = src
/obj/effect/landmark/ai_multicam_room/Destroy()
if(GLOB.ai_camera_room_landmark == src)
GLOB.ai_camera_room_landmark = null
return ..()
//Dummy camera eyes
/mob/observer/eye/aiEye/pic_in_pic
name = "Secondary AI Eye"
var/obj/screen/movable/pic_in_pic/ai/screen
var/list/cameras_telegraphed = list()
var/telegraph_cameras = TRUE
var/telegraph_range = 7
/mob/observer/eye/aiEye/pic_in_pic/GetViewerClient()
if(screen && screen.ai)
return screen.ai.client
/mob/observer/eye/aiEye/pic_in_pic/setLoc(turf/T)
T = get_turf(T)
forceMove(T)
if(screen && screen.ai)
screen.ai.camera_visibility(src)
else
cameranet.visibility(src)
update_camera_telegraphing()
/mob/observer/eye/aiEye/pic_in_pic/proc/update_camera_telegraphing()
if(!telegraph_cameras)
return
var/list/obj/machinery/camera/add = list()
var/list/obj/machinery/camera/remove = list()
var/list/obj/machinery/camera/visible = list()
for(var/VV in visibleChunks)
var/datum/chunk/camera/CC = VV
for(var/V in CC.cameras)
var/obj/machinery/camera/C = V
if (!C.can_use() || (get_dist(C, src) > telegraph_range))
continue
visible |= C
add = visible - cameras_telegraphed
remove = cameras_telegraphed - visible
for(var/V in remove)
var/obj/machinery/camera/C = V
if(QDELETED(C))
continue
cameras_telegraphed -= C
C.in_use_lights--
C.update_icon()
for(var/V in add)
var/obj/machinery/camera/C = V
if(QDELETED(C))
continue
cameras_telegraphed |= C
C.in_use_lights++
C.update_icon()
/mob/observer/eye/aiEye/pic_in_pic/proc/disable_camera_telegraphing()
telegraph_cameras = FALSE
for(var/V in cameras_telegraphed)
var/obj/machinery/camera/C = V
if(QDELETED(C))
continue
C.in_use_lights--
C.update_icon()
cameras_telegraphed.Cut()
/mob/observer/eye/aiEye/pic_in_pic/Destroy()
disable_camera_telegraphing()
if(screen && screen.ai)
screen.ai.all_eyes -= src
return ..()
//AI procs
/mob/living/silicon/ai/proc/drop_new_multicam(silent = FALSE)
if(!multicam_allowed)
if(!silent)
to_chat(src, "<span class='warning'>This action is currently disabled. Contact an administrator to enable this feature.</span>")
return
if(!eyeobj)
return
if(multicam_screens.len >= max_multicams)
if(!silent)
to_chat(src, "<span class='warning'>Cannot place more than [max_multicams] multicamera windows.</span>")
return
var/obj/screen/movable/pic_in_pic/ai/C = new /obj/screen/movable/pic_in_pic/ai()
C.set_view_size(3, 3, FALSE)
C.set_view_center(get_turf(eyeobj))
C.set_ai(src)
if(!silent)
to_chat(src, "<span class='notice'>Added new multicamera window.</span>")
return C
/mob/living/silicon/ai/proc/toggle_multicam()
if(!multicam_allowed)
to_chat(src, "<span class='warning'>This action is currently disabled. Contact an administrator to enable this feature.</span>")
return
if(multicam_on)
end_multicam()
else
start_multicam()
/mob/living/silicon/ai/proc/start_multicam()
if(multicam_on || aiRestorePowerRoutine || !isturf(loc))
return
if(!GLOB.ai_camera_room_landmark)
to_chat(src, "<span class='warning'>This function is not available at this time.</span>")
return
multicam_on = TRUE
refresh_multicam()
to_chat(src, "<span class='notice'>Multiple-camera viewing mode activated.</span>")
/mob/living/silicon/ai/proc/refresh_multicam()
reset_view(GLOB.ai_camera_room_landmark)
if(client)
for(var/V in multicam_screens)
var/obj/screen/movable/pic_in_pic/P = V
P.show_to(client)
/mob/living/silicon/ai/proc/end_multicam()
if(!multicam_on)
return
multicam_on = FALSE
select_main_multicam_window(null)
if(client)
for(var/V in multicam_screens)
var/obj/screen/movable/pic_in_pic/P = V
P.unshow_to(client)
reset_view()
to_chat(src, "<span class='notice'>Multiple-camera viewing mode deactivated.</span>")
/mob/living/silicon/ai/proc/select_main_multicam_window(obj/screen/movable/pic_in_pic/ai/P)
if(master_multicam == P)
return
if(master_multicam)
master_multicam.set_view_center(get_turf(eyeobj), FALSE)
master_multicam.unhighlight()
master_multicam = null
if(P)
P.highlight()
eyeobj.setLoc(get_turf(P.center))
P.set_view_center(eyeobj)
master_multicam = P

View File

@@ -410,7 +410,7 @@
return
/mob/living/silicon/reset_view()
..()
. = ..()
if(cameraFollow)
cameraFollow = null

View File

@@ -229,7 +229,7 @@
else
client.perspective = EYE_PERSPECTIVE
client.eye = loc
return
return 1
/mob/proc/show_inv(mob/user as mob)

BIN
icons/misc/pic_in_pic.dmi Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -154,6 +154,7 @@
#include "code\_onclick\hud\human.dm"
#include "code\_onclick\hud\movable_screen_objects.dm"
#include "code\_onclick\hud\other_mobs.dm"
#include "code\_onclick\hud\picture_in_picture.dm"
#include "code\_onclick\hud\radial.dm"
#include "code\_onclick\hud\radial_persistent.dm"
#include "code\_onclick\hud\robot.dm"
@@ -2526,6 +2527,7 @@
#include "code\modules\mob\living\silicon\ai\login.dm"
#include "code\modules\mob\living\silicon\ai\logout.dm"
#include "code\modules\mob\living\silicon\ai\malf.dm"
#include "code\modules\mob\living\silicon\ai\multicam.dm"
#include "code\modules\mob\living\silicon\decoy\death.dm"
#include "code\modules\mob\living\silicon\decoy\decoy.dm"
#include "code\modules\mob\living\silicon\decoy\life.dm"