From 9c4f4ce17f361d57e0ae65ec077a15a28205d5a4 Mon Sep 17 00:00:00 2001 From: Cruix Date: Wed, 30 May 2018 18:31:32 -0700 Subject: [PATCH] AI Multicamera mode (#37695) * Added multicamera mode for AIs * Minor multicamera fixes * Cameras near an AI multicamera eye now light up red * Disabled AI multicamera mode without admin intervention * Fixed AIs being able to use multicamera mode when they should not --- _maps/map_files/generic/CentCom.dmm | 1068 +++++++++-------- code/_onclick/ai.dm | 8 + code/_onclick/hud/_defines.dm | 2 + code/_onclick/hud/ai.dm | 30 +- code/_onclick/hud/movable_screen_objects.dm | 6 +- code/_onclick/hud/picture_in_picture.dm | 144 +++ code/_onclick/hud/screen_objects.dm | 15 + code/datums/holocall.dm | 5 +- code/game/machinery/camera/camera.dm | 3 +- .../machinery/computer/camera_advanced.dm | 2 +- code/modules/lighting/lighting_object.dm | 2 - code/modules/mob/living/silicon/ai/ai.dm | 23 +- .../living/silicon/ai/freelook/cameranet.dm | 70 +- .../mob/living/silicon/ai/freelook/chunk.dm | 18 +- .../mob/living/silicon/ai/freelook/eye.dm | 39 +- code/modules/mob/living/silicon/ai/life.dm | 1 + code/modules/mob/living/silicon/ai/login.dm | 2 + .../modules/mob/living/silicon/ai/multicam.dm | 266 ++++ icons/misc/pic_in_pic.dmi | Bin 0 -> 3370 bytes icons/mob/screen_ai.dmi | Bin 2398 -> 2543 bytes icons/obj/machines/camera.dmi | Bin 6266 -> 8713 bytes icons/turf/areas.dmi | Bin 36693 -> 36973 bytes tgstation.dme | 2 + 23 files changed, 1126 insertions(+), 580 deletions(-) create mode 100644 code/_onclick/hud/picture_in_picture.dm create mode 100644 code/modules/mob/living/silicon/ai/multicam.dm create mode 100644 icons/misc/pic_in_pic.dmi diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 0f9857ad73..02c20e6896 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -5699,6 +5699,10 @@ }, /turf/open/floor/plasteel/cafeteria, /area/centcom/holding) +"pW" = ( +/obj/effect/landmark/ai_multicam_room, +/turf/open/ai_visible, +/area/ai_multicam_room) "pX" = ( /obj/item/storage/crayons, /obj/structure/table, @@ -10234,6 +10238,9 @@ dir = 8 }, /area/centcom/evac) +"Di" = ( +/turf/closed/indestructible/riveted, +/area/ai_multicam_room) "Dj" = ( /obj/machinery/vr_sleeper{ dir = 8 @@ -13624,6 +13631,9 @@ "PY" = ( /turf/open/floor/plasteel/whitegreen, /area/centcom/holding) +"Qe" = ( +/turf/open/ai_visible, +/area/ai_multicam_room) "Qk" = ( /obj/structure/flora/ausbushes/fullgrass, /obj/structure/flora/ausbushes/ppflowers, @@ -30581,29 +30591,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di aa aa aa @@ -30838,29 +30848,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -31095,29 +31105,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -31352,29 +31362,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -31609,29 +31619,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -31866,29 +31876,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -32123,29 +32133,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -32380,29 +32390,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -32637,29 +32647,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -32894,29 +32904,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -33151,29 +33161,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -33408,29 +33418,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +pW +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -33665,29 +33675,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -33922,29 +33932,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -34179,29 +34189,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -34436,29 +34446,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -34693,29 +34703,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -34950,29 +34960,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -35207,29 +35217,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -35464,29 +35474,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -35721,29 +35731,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -35978,29 +35988,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Qe +Di aa aa aa @@ -36235,29 +36245,29 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di +Di aa aa aa diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 9ef689de8b..e15d4f035c 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -26,6 +26,14 @@ return next_click = world.time + 1 + 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(check_click_intercept(params,A)) return diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 3225252520..b558e7c87c 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -134,6 +134,8 @@ #define ui_ai_take_picture "SOUTH:6,WEST+12" #define ui_ai_view_images "SOUTH:6,WEST+13" #define ui_ai_sensor "SOUTH:6,WEST+14" +#define ui_ai_multicam "SOUTH+1:6,WEST+13" +#define ui_ai_add_multicam "SOUTH+1:6,WEST+14" //Pop-up inventory #define ui_shoes "WEST+1:8,SOUTH:5" diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index 8d4aca5fbf..463051c6b4 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -164,6 +164,26 @@ var/mob/living/silicon/S = usr S.toggle_sensors() +/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/ai ui_style_icon = 'icons/mob/screen_ai.dmi' @@ -247,12 +267,20 @@ using.screen_loc = ui_ai_view_images static_inventory += using - //Medical/Security sensors using = new /obj/screen/ai/sensors() using.screen_loc = ui_ai_sensor static_inventory += using +//Multicamera mode + using = new /obj/screen/ai/multicam() + using.screen_loc = ui_ai_multicam + static_inventory += using + +//Add multicamera camera + using = new /obj/screen/ai/add_multicam() + using.screen_loc = ui_ai_add_multicam + static_inventory += using /mob/living/silicon/ai/create_mob_hud() if(client && !hud_used) diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm index 4da721012d..f71ced7a9d 100644 --- a/code/_onclick/hud/movable_screen_objects.dm +++ b/code/_onclick/hud/movable_screen_objects.dm @@ -12,6 +12,8 @@ var/snap2grid = FALSE var/moved = FALSE var/locked = FALSE + var/x_off = -16 + var/y_off = -16 //Snap Screen Object //Tied to the grid, snaps to the nearest turf @@ -42,8 +44,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]" moved = screen_loc diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/picture_in_picture.dm new file mode 100644 index 0000000000..ce5f2c9884 --- /dev/null +++ b/code/_onclick/hud/picture_in_picture.dm @@ -0,0 +1,144 @@ +/obj/screen/movable/pic_in_pic + name = "Picture-in-picture" + screen_loc = "CENTER" + plane = GAME_PLANE + 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 = SPACE_LAYER + +/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 = HUD_PLANE + var/matrix/M = matrix() + M.Translate(0, (height + 0.25) * world.icon_size) + move_tab.transform = M + add_overlay(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 = HUD_PLANE + 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 = HUD_PLANE + 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 = HUD_PLANE + 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 + add_overlay(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 + + cut_overlays() + 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 diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 02ece81b41..d2ac10a83f 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -30,6 +30,9 @@ /obj/screen/orbit() return +/obj/screen/proc/component_click(obj/screen/component_button/component, params) + return + /obj/screen/text icon = null icon_state = null @@ -604,3 +607,15 @@ holder.screen -= src holder = null return ..() + + +/obj/screen/component_button + var/obj/screen/parent + +/obj/screen/component_button/Initialize(mapload, obj/screen/parent) + . = ..() + src.parent = parent + +/obj/screen/component_button/Click(params) + if(parent) + parent.component_click(src, params) diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index d19e8be2e1..22dc6a7671 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -60,9 +60,6 @@ user.remote_control = null if(!QDELETED(eye)) - if(user_good && user.client) - for(var/datum/camerachunk/chunk in eye.visibleCameraChunks) - chunk.remove(eye) qdel(eye) eye = null @@ -284,7 +281,7 @@ else var/datum/preset_holoimage/H = new preset_image_type record.caller_image = H.build_image() - + //These build caller image from outfit and some additional data, for use by mappers for ruin holorecords /datum/preset_holoimage var/nonhuman_mobtype //Fill this if you just want something nonhuman diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 3e8a0bb84d..2ea74637d5 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -36,6 +36,7 @@ var/alarm_on = FALSE var/busy = FALSE var/emped = FALSE //Number of consecutive EMP's on this camera + var/in_use_lights = 0 // Upgrades bitflag var/upgrades = 0 @@ -285,7 +286,7 @@ else if (stat & EMPED) icon_state = "[initial(icon_state)]emp" else - icon_state = "[initial(icon_state)]" + icon_state = "[initial(icon_state)][in_use_lights ? "_in_use" : ""]" /obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = 1) status = !status diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 6112a5d760..26a8ac940e 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -190,7 +190,7 @@ else moveToNullspace() if(use_static) - GLOB.cameranet.visibility(src) + GLOB.cameranet.visibility(src, GetViewerClient()) if(visible_icon) if(eye_user.client) eye_user.client.images -= user_image diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index fe25dc9439..c0c5a411e7 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -11,8 +11,6 @@ layer = LIGHTING_LAYER invisibility = INVISIBILITY_LIGHTING - blend_mode = BLEND_ADD - var/needs_update = FALSE var/turf/myturf diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 8664a111ae..cbc04d11be 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -74,7 +74,7 @@ var/nuking = FALSE var/obj/machinery/doomsday_device/doomsday_device - var/mob/camera/aiEye/eyeobj = new + var/mob/camera/aiEye/eyeobj var/sprint = 10 var/cooldown = 0 var/acceleration = 1 @@ -85,6 +85,13 @@ var/datum/action/innate/deploy_last_shell/redeploy_action = new var/chnotify = 0 + var/multicam_allowed = FALSE + 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 + /mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai) . = ..() if(!target_ai) //If there is no player/brain inside. @@ -116,8 +123,7 @@ job = "AI" - eyeobj.ai = src - eyeobj.forceMove(src.loc) + create_eye() rename_self("ai") holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"default")) @@ -871,9 +877,12 @@ current = A if(client) if(ismovableatom(A)) + if(A != GLOB.ai_camera_room_landmark) + end_multicam() client.perspective = EYE_PERSPECTIVE client.eye = A else + end_multicam() if(isturf(loc)) if(eyeobj) client.eye = eyeobj @@ -993,3 +1002,11 @@ . = ..() if(!target_ai) 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) + +/mob/living/silicon/ai/forceMove(atom/destination) + . = ..() + if(.) + end_multicam() diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index 815ddb64ea..5cc08ff1e0 100644 --- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm +++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm @@ -38,29 +38,63 @@ 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(mob/camera/aiEye/ai) - // 0xf = 15 - var/x1 = max(0, ai.x - 16) & ~(CHUNK_SIZE - 1) - var/y1 = max(0, ai.y - 16) & ~(CHUNK_SIZE - 1) - var/x2 = min(world.maxx, ai.x + 16) & ~(CHUNK_SIZE - 1) - var/y2 = min(world.maxy, ai.y + 16) & ~(CHUNK_SIZE - 1) +/datum/cameranet/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/visibleChunks = list() + var/list/chunks_pre_seen = list() + var/list/chunks_post_seen = list() - for(var/x = x1; x <= x2; x += CHUNK_SIZE) - for(var/y = y1; y <= y2; y += CHUNK_SIZE) - visibleChunks |= getCameraChunk(x, y, ai.z) + for(var/V in moved_eyes) + var/mob/camera/aiEye/eye = V + if(C) + chunks_pre_seen |= eye.visibleCameraChunks + // 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) - var/list/remove = ai.visibleCameraChunks - visibleChunks - var/list/add = visibleChunks - ai.visibleCameraChunks + var/list/visibleChunks = list() - for(var/chunk in remove) - var/datum/camerachunk/c = chunk - c.remove(ai) + 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/chunk in add) - var/datum/camerachunk/c = chunk - c.add(ai) + var/list/remove = eye.visibleCameraChunks - visibleChunks + var/list/add = visibleChunks - eye.visibleCameraChunks + + for(var/chunk in remove) + var/datum/camerachunk/c = chunk + c.remove(eye, FALSE) + + for(var/chunk in add) + var/datum/camerachunk/c = chunk + c.add(eye, FALSE) + + if(C) + chunks_post_seen |= eye.visibleCameraChunks + + if(C) + for(var/V in other_eyes) + var/mob/camera/aiEye/eye = V + chunks_post_seen |= eye.visibleCameraChunks + + 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/camerachunk/c = chunk + C.images -= c.obscured + + for(var/chunk in add) + var/datum/camerachunk/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. diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm index f062c7e010..e03bba3067 100644 --- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm +++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm @@ -20,10 +20,11 @@ // Add an AI eye to the chunk, then update if changed. -/datum/camerachunk/proc/add(mob/camera/aiEye/eye) - var/client/client = eye.GetViewerClient() - if(client) - client.images += obscured +/datum/camerachunk/proc/add(mob/camera/aiEye/eye, add_images = TRUE) + if(add_images) + var/client/client = eye.GetViewerClient() + if(client) + client.images += obscured eye.visibleCameraChunks += src visible++ seenby += eye @@ -32,10 +33,11 @@ // Remove an AI eye from the chunk, then update if changed. -/datum/camerachunk/proc/remove(mob/camera/aiEye/eye) - var/client/client = eye.GetViewerClient() - if(client) - client.images -= obscured +/datum/camerachunk/proc/remove(mob/camera/aiEye/eye, remove_images = TRUE) + if(remove_images) + var/client/client = eye.GetViewerClient() + if(client) + client.images -= obscured eye.visibleCameraChunks -= src seenby -= eye if(visible > 0) diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index b5b1bd082b..469639d58f 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -11,6 +11,7 @@ var/mob/living/silicon/ai/ai = null var/relay_speech = FALSE var/use_static = TRUE + var/static_visibility_range = 16 // Use this when setting the aiEye's location. // It will also stream the chunk that the new loc is in. @@ -25,8 +26,8 @@ else moveToNullspace() // ???? if(use_static) - GLOB.cameranet.visibility(src) - if(ai.client) + ai.camera_visibility(src) + if(ai.client && !ai.multicam_on) ai.client.eye = src update_parallax_contents() //Holopad @@ -35,6 +36,8 @@ H.move_hologram(ai, T) if(ai.camera_light_on) ai.light_cameras() + if(ai.master_multicam) + ai.master_multicam.refresh_view() /mob/camera/aiEye/Move() return 0 @@ -45,20 +48,27 @@ return null /mob/camera/aiEye/proc/RemoveImages() - if(use_static) - for(var/datum/camerachunk/chunk in visibleCameraChunks) - chunk.remove(src) + var/client/C = GetViewerClient() + if(C && use_static) + for(var/V in visibleCameraChunks) + var/datum/camerachunk/c = V + C.images -= c.obscured /mob/camera/aiEye/Destroy() - ai = null + if(ai) + ai.all_eyes -= src + ai = null + for(var/V in visibleCameraChunks) + var/datum/camerachunk/c = V + c.remove(src) return ..() /atom/proc/move_camera_by_click() if(isAI(usr)) 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)) && (AI.eyeobj.z == z)) AI.cameraFollow = null - if (isturf(src.loc) || isturf(src)) + if (isturf(loc) || isturf(src)) AI.eyeobj.setLoc(src) // This will move the AIEye. It will also cause lights near the eye to light up, if toggled. @@ -95,12 +105,19 @@ if(!eyeobj || !eyeobj.loc || QDELETED(eyeobj)) to_chat(src, "ERROR: Eyeobj not found. Creating new eye...") - eyeobj = new(loc) - eyeobj.ai = src - eyeobj.name = "[src.name] (AI Eye)" // Give it a name + create_eye() eyeobj.setLoc(loc) +/mob/living/silicon/ai/proc/create_eye() + if(eyeobj) + return + eyeobj = new /mob/camera/aiEye() + all_eyes += eyeobj + eyeobj.ai = src + eyeobj.setLoc(loc) + eyeobj.name = "[name] (AI Eye)" + /mob/living/silicon/ai/verb/toggle_acceleration() set category = "AI Commands" set name = "Toggle Camera Acceleration" diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index a060c899d9..0fd33110b6 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -96,6 +96,7 @@ /mob/living/silicon/ai/proc/start_RestorePowerRoutine() to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.") + end_multicam() sleep(50) var/turf/T = get_turf(src) var/area/AIarea = get_area(src) diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm index c771f60aa2..2dc9a1fb1c 100644 --- a/code/modules/mob/living/silicon/ai/login.dm +++ b/code/modules/mob/living/silicon/ai/login.dm @@ -4,4 +4,6 @@ for(var/obj/machinery/ai_status_display/O in GLOB.ai_status_displays) //change status O.mode = 1 O.emotion = "Neutral" + if(multicam_on) + end_multicam() view_core() diff --git a/code/modules/mob/living/silicon/ai/multicam.dm b/code/modules/mob/living/silicon/ai/multicam.dm new file mode 100644 index 0000000000..b6f3724fcf --- /dev/null +++ b/code/modules/mob/living/silicon/ai/multicam.dm @@ -0,0 +1,266 @@ +//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/camera/aiEye/pic_in_pic/aiEye + +/obj/screen/movable/pic_in_pic/ai/Initialize() + . = ..() + aiEye = new /mob/camera/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 = SPACE_LAYER + +/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 + add_overlay(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 + cut_overlay(standard_background) + add_overlay(highlighted_background) + +/obj/screen/movable/pic_in_pic/ai/proc/unhighlight() + if(!highlighted) + return + highlighted = FALSE + cut_overlay(highlighted_background) + add_overlay(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/open/ai_visible + name = "" + icon = 'icons/misc/pic_in_pic.dmi' + icon_state = "room_background" + flags_1 = NOJAUNT_1 + +/turf/open/ai_visible/Initialize() + . = ..() + obscured = image(null, src, null) + +/area/ai_multicam_room + name = "ai_multicam_room" + icon_state = "ai_camera_room" + dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + valid_territory = FALSE + ambientsounds = list() + blob_allowed = FALSE + noteleport = TRUE + hidden = TRUE + safe = TRUE + +GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) + +/obj/effect/landmark/ai_multicam_room + name = "ai camera room" + icon = 'icons/mob/landmarks.dmi' + 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/camera/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/camera/aiEye/pic_in_pic/GetViewerClient() + if(screen && screen.ai) + return screen.ai.client + +/mob/camera/aiEye/pic_in_pic/setLoc(turf/T) + if (T) + forceMove(T) + else + moveToNullspace() + if(screen && screen.ai) + screen.ai.camera_visibility(src) + else + GLOB.cameranet.visibility(src) + update_camera_telegraphing() + +/mob/camera/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 visibleCameraChunks) + var/datum/camerachunk/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/camera/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/camera/aiEye/pic_in_pic/Destroy() + disable_camera_telegraphing() + return ..() + +//AI procs + +/mob/living/silicon/ai/proc/drop_new_multicam(silent = FALSE) + if(!multicam_allowed) + if(!silent) + to_chat(src, "This action is currently disabled. Contact an administrator to enable this feature.") + return + if(!eyeobj) + return + if(multicam_screens.len >= max_multicams) + if(!silent) + to_chat(src, "Cannot place more than [max_multicams] multicamera windows.") + 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, "Added new multicamera window.") + return C + +/mob/living/silicon/ai/proc/toggle_multicam() + if(!multicam_allowed) + to_chat(src, "This action is currently disabled. Contact an administrator to enable this feature.") + 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, "This function is not available at this time.") + return + multicam_on = TRUE + refresh_multicam() + to_chat(src, "Multiple-camera viewing mode activated.") + +/mob/living/silicon/ai/proc/refresh_multicam() + reset_perspective(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_perspective() + to_chat(src, "Multiple-camera viewing mode deactivated.") + + +/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 diff --git a/icons/misc/pic_in_pic.dmi b/icons/misc/pic_in_pic.dmi new file mode 100644 index 0000000000000000000000000000000000000000..9c550f3c25ecdf6817afd63dc0843875e245dfaa GIT binary patch literal 3370 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 E2DHmSY5)KL literal 0 HcmV?d00001 diff --git a/icons/mob/screen_ai.dmi b/icons/mob/screen_ai.dmi index a449cad90645421b1dd90f63cfa4efe0bda8fd28..8388ea3f806c07006c698401c977fe138e692670 100644 GIT binary patch delta 2446 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 diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi index 4e78e0444b5710ed8f676b98cf9616f3ea44fa88..1fadd875d70efa82332cb354b720d3c3f0c1eb6d 100644 GIT binary patch delta 6902 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 Qi3@b*wEb_mld!x01q!+I&;S4c delta 6620 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 U{-<|3sDqq8bNMv!r1QQ11Da`5Hvj+t diff --git a/tgstation.dme b/tgstation.dme index 909f5fdd99..e0a84f22a5 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -181,6 +181,7 @@ #include "code\_onclick\hud\monkey.dm" #include "code\_onclick\hud\movable_screen_objects.dm" #include "code\_onclick\hud\parallax.dm" +#include "code\_onclick\hud\picture_in_picture.dm" #include "code\_onclick\hud\plane_master.dm" #include "code\_onclick\hud\revenanthud.dm" #include "code\_onclick\hud\robot.dm" @@ -1949,6 +1950,7 @@ #include "code\modules\mob\living\silicon\ai\life.dm" #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\multicam.dm" #include "code\modules\mob\living\silicon\ai\say.dm" #include "code\modules\mob\living\silicon\ai\vox_sounds.dm" #include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm"