Files
Polaris/code/modules/mob/freelook/chunk.dm
Rykka cc038370f9 Add AI Picture-in-Picture Mode!
This adds a system for picture-in-picture UI windows using
vis_contents. Essentially, it allows you to make UI windows that show an area of turfs. It also refactors how cameranet visibility works.

Currently, this is implemented on AIs. They gain two new UI buttons - "Enter Multicam Mode", and "Create Multicam". When they go into Multicam Mode, they see a background of animated binary numbers, and they are allowed to create an infinite amount of these picture in picture windows, which subsequently creates an AI Eye for each one. They are able to control each AI eye individually, by first clicking on the PIP window to select it as "active" and then using the normal arrow key controls. The PIP windows can be freely resized and moved around the area.

You can control everything inside these PIP windows EXACTLY the same as you can using a traditional AI Eye, as demonstrated below.

For admins, there is a config option to disable PIP entirely - simply set `var/multicam_allowed = TRUE` to FALSE if you wish to disable it from being used. (Please be reasonable.) <3

You can see an example of how this works here:
![https://i.imgur.com/TCND8W9.mp4](https://i.imgur.com/TCND8W9.mp4)

AI Multicam functionality.

Do note that if the lightbulb in the AI core is busted, the multicam room is dark(er) than it would be, but you can still see your camera windows just fine. (I'll probably fix this later.) It only affects the "matrix" backdrop, the PIP windows are still fine.

This has been runtime-tested with the latest `master` revision and produces 0 runtimes, and has no noticeable impact on server CPU usage.

Polarisport is here! Port of https://github.com/VOREStation/VOREStation/pull/7752
2020-05-10 14:00:45 -04:00

155 lines
4.0 KiB
Plaintext

#define UPDATE_BUFFER 25 // 2.5 seconds
// CHUNK
//
// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
// Allows the Eye to stream these chunks and know what it can and cannot see.
/datum/obfuscation
var/icon = 'icons/effects/cameravis.dmi'
var/icon_state = "black"
/datum/chunk
var/list/obscuredTurfs = list()
var/list/visibleTurfs = list()
var/list/obscured = list()
var/list/turfs = list()
var/list/seenby = list()
var/visible = 0
var/changed = 0
var/updating = 0
var/x = 0
var/y = 0
var/z = 0
var/datum/obfuscation/obfuscation = new()
// Add an eye to the chunk, then update if changed.
/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
visible++
seenby += eye
if(changed && !updating)
update()
// Remove an eye from the chunk, then update if changed.
/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
seenby -= eye
if(visible > 0)
visible--
// Called when a chunk has changed. I.E: A wall was deleted.
/datum/chunk/proc/visibilityChanged(turf/loc)
if(!visibleTurfs[loc])
return
hasChanged()
// 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.
/datum/chunk/proc/hasChanged(var/update_now = 0)
if(visible || update_now)
if(!updating)
updating = 1
spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once
update()
updating = 0
else
changed = 1
// The actual updating.
/datum/chunk/proc/update()
set background = 1
var/list/newVisibleTurfs = new()
acquireVisibleTurfs(newVisibleTurfs)
// Removes turf that isn't in turfs.
newVisibleTurfs &= turfs
var/list/visAdded = newVisibleTurfs - visibleTurfs
var/list/visRemoved = visibleTurfs - newVisibleTurfs
visibleTurfs = newVisibleTurfs
obscuredTurfs = turfs - newVisibleTurfs
for(var/turf in visAdded)
var/turf/t = turf
if(t.obfuscations[obfuscation.type])
obscured -= t.obfuscations[obfuscation.type]
for(var/eye in seenby)
var/mob/observer/eye/m = eye
if(!m)
continue
var/client/client = m.GetViewerClient()
if(client)
client.images -= t.obfuscations[obfuscation.type]
for(var/turf in visRemoved)
var/turf/t = turf
if(obscuredTurfs[t])
LAZYINITLIST(t.obfuscations)
if(!t.obfuscations[obfuscation.type])
var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER)
ob_image.plane = PLANE_FULLSCREEN
t.obfuscations[obfuscation.type] = ob_image
obscured += t.obfuscations[obfuscation.type]
for(var/eye in seenby)
var/mob/observer/eye/m = eye
if(!m)
seenby -= m
continue
var/client/client = m.GetViewerClient()
if(client)
client.images += t.obfuscations[obfuscation.type]
/datum/chunk/proc/acquireVisibleTurfs(var/list/visible)
// Create a new camera chunk, since the chunks are made as they are needed.
/datum/chunk/New(loc, x, y, z)
// 0xf = 15
x &= ~0xf
y &= ~0xf
src.x = x
src.y = y
src.z = z
for(var/turf/t in range(10, locate(x + 8, y + 8, z)))
if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16)
turfs[t] = t
acquireVisibleTurfs(visibleTurfs)
// Removes turf that isn't in turfs.
visibleTurfs &= turfs
obscuredTurfs = turfs - visibleTurfs
for(var/turf in obscuredTurfs)
var/turf/t = turf
LAZYINITLIST(t.obfuscations)
if(!t.obfuscations[obfuscation.type])
var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER)
ob_image.plane = PLANE_FULLSCREEN
t.obfuscations[obfuscation.type] = ob_image
obscured += t.obfuscations[obfuscation.type]
#undef UPDATE_BUFFER