mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-25 21:19:00 +01:00
Camera net/chunk refactor. Slight performance improvements and makes it GC properly (#20302)
* Add SScamera * Refactor camera * Refactor how camera chunks add/remove cams * Use CAMERA_VIEW_DISTANCE to determine max view dist of cam * Fix movable cameras and optimise them * Optimise update() * Ensure aiEyes get removed properly when deleted * Final optimisations and refactoring * Fix cameras being EMPed * Shits fixed * AA review * Fix merge mistake * Add comment about early return * Seans review * the it the Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> * Update code/game/machinery/camera/camera.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Ensure the robit has a camera before updating it --------- Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com> Co-authored-by: Charlie <69320440+hal9000PR@users.noreply.github.com> Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com>
This commit is contained in:
co-authored by
S34N
Ryan
Charlie
parent
4a1873023a
commit
0fedbb9257
@@ -504,6 +504,11 @@
|
||||
///from base power_change() when power is restored
|
||||
#define COMSIG_MACHINERY_POWER_RESTORED "machinery_power_restored"
|
||||
|
||||
// /obj/machinery/camera signals
|
||||
#define COMSIG_CAMERA_ON "camera_on"
|
||||
#define COMSIG_CAMERA_OFF "camera_off"
|
||||
#define COMSIG_CAMERA_MOVED "camera_moved"
|
||||
|
||||
// /obj/item signals
|
||||
|
||||
///from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
|
||||
|
||||
@@ -153,6 +153,8 @@
|
||||
|
||||
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
|
||||
|
||||
#define CAMERA_VIEW_DISTANCE 7
|
||||
#define CAMERA_CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc..
|
||||
|
||||
|
||||
//Turf wet states
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
#define FIRE_PRIORITY_GARBAGE 15
|
||||
#define FIRE_PRIORITY_AIR 20
|
||||
#define FIRE_PRIORITY_NPC 20
|
||||
#define FIRE_PRIORITY_CAMERA 20
|
||||
#define FIRE_PRIORITY_PROCESS 25
|
||||
#define FIRE_PRIORITY_THROWING 25
|
||||
#define FIRE_PRIORITY_SPACEDRIFT 30
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Subsystem that controls the camera vision tiles
|
||||
SUBSYSTEM_DEF(camera)
|
||||
name = "Camera"
|
||||
flags = SS_BACKGROUND | SS_NO_INIT
|
||||
priority = FIRE_PRIORITY_CAMERA
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
wait = 2.5 SECONDS
|
||||
offline_implications = "AI camera view won't update. No immediate action is needed."
|
||||
var/list/chunk_queue = list()
|
||||
|
||||
/datum/controller/subsystem/camera/fire(resumed)
|
||||
for(var/datum/camerachunk/chunk as anything in chunk_queue)
|
||||
chunk.update()
|
||||
chunk_queue -= chunk
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
|
||||
/datum/controller/subsystem/camera/proc/queue(datum/camerachunk/chunk)
|
||||
if(!chunk_queue[chunk])
|
||||
chunk_queue[chunk] = chunk
|
||||
|
||||
/datum/controller/subsystem/camera/proc/remove_from_queue(datum/camerachunk/chunk)
|
||||
chunk_queue -= chunk
|
||||
@@ -14,6 +14,7 @@
|
||||
max_integrity = 100
|
||||
integrity_failure = 50
|
||||
var/list/network = list("SS13")
|
||||
var/list/previous_network
|
||||
var/c_tag = null
|
||||
var/c_tag_order = 999
|
||||
var/status = TRUE
|
||||
@@ -21,10 +22,12 @@
|
||||
var/start_active = FALSE //If it ignores the random chance to start broken on round start
|
||||
var/invuln = null
|
||||
var/obj/item/camera_assembly/assembly = null
|
||||
/// If this camera should be added to the camera network and update the camera network when it moves around
|
||||
var/part_of_camera_network
|
||||
|
||||
//OTHER
|
||||
|
||||
var/view_range = 7
|
||||
var/view_range = CAMERA_VIEW_DISTANCE
|
||||
var/short_range = 2
|
||||
|
||||
var/alarm_on = FALSE
|
||||
@@ -34,7 +37,7 @@
|
||||
var/toggle_sound = 'sound/items/wirecutter.ogg'
|
||||
blocks_emissive = EMISSIVE_BLOCK_GENERIC
|
||||
|
||||
/obj/machinery/camera/Initialize(mapload)
|
||||
/obj/machinery/camera/Initialize(mapload, should_add_to_cameranet = TRUE)
|
||||
. = ..()
|
||||
wires = new(src)
|
||||
assembly = new(src)
|
||||
@@ -43,22 +46,27 @@
|
||||
assembly.update_icon()
|
||||
|
||||
GLOB.cameranet.cameras += src
|
||||
GLOB.cameranet.addCamera(src)
|
||||
part_of_camera_network = should_add_to_cameranet
|
||||
if(part_of_camera_network)
|
||||
GLOB.cameranet.addCamera(src)
|
||||
if(isturf(loc))
|
||||
LAZYADD(get_area(src).cameras, UID())
|
||||
if(is_station_level(z) && prob(3) && !start_active)
|
||||
toggle_cam(null, FALSE)
|
||||
turn_off(null, FALSE)
|
||||
wires.cut_all()
|
||||
|
||||
/obj/machinery/camera/proc/set_area_motion(area/A)
|
||||
area_motion = A
|
||||
|
||||
/obj/machinery/camera/Moved(atom/OldLoc, Dir, Forced)
|
||||
. = ..()
|
||||
SEND_SIGNAL(src, COMSIG_CAMERA_MOVED, OldLoc)
|
||||
|
||||
/obj/machinery/camera/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
toggle_cam(null, FALSE) //kick anyone viewing out
|
||||
kick_out_watchers()
|
||||
QDEL_NULL(assembly)
|
||||
QDEL_NULL(wires)
|
||||
GLOB.cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that
|
||||
GLOB.cameranet.cameras -= src
|
||||
if(isarea(get_area(src)))
|
||||
LAZYREMOVE(get_area(src).cameras, UID())
|
||||
@@ -85,36 +93,26 @@
|
||||
return
|
||||
if(!isEmpProof())
|
||||
if(prob(150/severity))
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
var/list/previous_network = network
|
||||
network = list()
|
||||
GLOB.cameranet.removeCamera(src)
|
||||
stat |= EMPED
|
||||
set_light(0)
|
||||
emped = emped+1 //Increase the number of consecutive EMP's
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
var/thisemp = emped //Take note of which EMP this proc is for
|
||||
spawn(900)
|
||||
if(!QDELETED(src))
|
||||
if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again
|
||||
network = previous_network
|
||||
stat &= ~EMPED
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
if(can_use())
|
||||
GLOB.cameranet.addCamera(src)
|
||||
emped = 0 //Resets the consecutive EMP count
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client && M.client.eye == src)
|
||||
M.reset_perspective(null)
|
||||
to_chat(M, "The screen bursts into static.")
|
||||
if(!(stat & EMPED))
|
||||
previous_network = network
|
||||
network = list()
|
||||
stat |= EMPED
|
||||
turn_off(null, FALSE, TRUE)
|
||||
addtimer(CALLBACK(src, PROC_REF(reactivate_after_emp)), 90 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
|
||||
..()
|
||||
|
||||
/obj/machinery/camera/proc/reactivate_after_emp()
|
||||
network = previous_network
|
||||
previous_network = null
|
||||
stat &= ~EMPED
|
||||
turn_on(null, FALSE, TRUE)
|
||||
|
||||
/obj/machinery/camera/ex_act(severity)
|
||||
if(invuln)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/machinery/camera/proc/setViewRange(num = 7)
|
||||
/obj/machinery/camera/proc/setViewRange(num = CAMERA_VIEW_DISTANCE)
|
||||
view_range = num
|
||||
GLOB.cameranet.updateVisibility(src, 0)
|
||||
|
||||
@@ -261,34 +259,56 @@
|
||||
else
|
||||
icon_state = "[initial(icon_state)]"
|
||||
|
||||
/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE)
|
||||
status = !status
|
||||
if(can_use())
|
||||
GLOB.cameranet.addCamera(src)
|
||||
if(isturf(loc))
|
||||
LAZYADD(get_area(src).cameras, UID())
|
||||
else
|
||||
set_light(0)
|
||||
GLOB.cameranet.removeCamera(src)
|
||||
if(isarea(get_area(src)))
|
||||
LAZYREMOVE(get_area(src).cameras, UID())
|
||||
GLOB.cameranet.updateChunk(x, y, z)
|
||||
var/change_msg = "deactivates"
|
||||
/obj/machinery/camera/proc/toggle_cam(mob/user, display_message = TRUE)
|
||||
if(status)
|
||||
change_msg = "reactivates"
|
||||
if(displaymessage)
|
||||
turn_off(user, display_message)
|
||||
return
|
||||
|
||||
turn_on(user, display_message)
|
||||
|
||||
/obj/machinery/camera/proc/turn_on(mob/user, display_message = TRUE, emp_recover = FALSE)
|
||||
if(status && !emp_recover)
|
||||
return
|
||||
status = TRUE
|
||||
if(!emp_recover && isturf(loc))
|
||||
LAZYADD(get_area(src).cameras, UID())
|
||||
|
||||
if(display_message)
|
||||
if(user)
|
||||
visible_message("<span class='danger'>[user] [change_msg] [src]!</span>")
|
||||
visible_message("<span class='danger'>[user] reactivates [src]!</span>")
|
||||
add_hiddenprint(user)
|
||||
else
|
||||
visible_message("<span class='danger'>\The [src] [change_msg]!</span>")
|
||||
visible_message("<span class='danger'>\The [src] reactivates!</span>")
|
||||
playsound(loc, toggle_sound, 100, TRUE)
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
SEND_SIGNAL(src, COMSIG_CAMERA_ON, user, display_message)
|
||||
|
||||
/obj/machinery/camera/proc/turn_off(mob/user, display_message = TRUE, emped = FALSE)
|
||||
if(!status && !emped)
|
||||
return
|
||||
|
||||
if(!emped)
|
||||
status = FALSE
|
||||
if(isarea(get_area(src)))
|
||||
LAZYREMOVE(get_area(src).cameras, UID())
|
||||
|
||||
set_light(0)
|
||||
|
||||
if(display_message)
|
||||
if(user)
|
||||
visible_message("<span class='danger'>[user] deactivates [src]!</span>")
|
||||
add_hiddenprint(user)
|
||||
else
|
||||
visible_message("<span class='danger'>\The [src] deactivates!</span>")
|
||||
playsound(loc, toggle_sound, 100, 1)
|
||||
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
|
||||
// now disconnect anyone using the camera
|
||||
//Apparently, this will disconnect anyone even if the camera was re-activated.
|
||||
//I guess that doesn't matter since they can't use it anyway?
|
||||
kick_out_watchers()
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_CAMERA_OFF, user, display_message, emped)
|
||||
|
||||
/obj/machinery/camera/proc/kick_out_watchers()
|
||||
for(var/mob/O in GLOB.player_list)
|
||||
if(O.client && O.client.eye == src)
|
||||
O.reset_perspective(null)
|
||||
@@ -395,6 +415,12 @@
|
||||
assembly.update_icon()
|
||||
|
||||
/obj/machinery/camera/portable/process() //Updates whenever the camera is moved.
|
||||
if(GLOB.cameranet && get_turf(src) != prev_turf)
|
||||
GLOB.cameranet.updatePortableCamera(src)
|
||||
prev_turf = get_turf(src)
|
||||
if(!part_of_camera_network)
|
||||
return PROCESS_KILL // Stop wasting performance
|
||||
|
||||
if(get_turf(src) == prev_turf)
|
||||
return
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_CAMERA_MOVED, prev_turf)
|
||||
GLOB.cameranet.updatePortableCamera(src, prev_turf)
|
||||
prev_turf = get_turf(src)
|
||||
|
||||
@@ -96,16 +96,15 @@
|
||||
var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])"
|
||||
input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag))
|
||||
state = ASSEMBLY_BUILT
|
||||
var/obj/machinery/camera/C = new(loc)
|
||||
var/list/network_list = uniquelist(tempnetwork)
|
||||
var/list/visible_networks = difflist(network_list, GLOB.restricted_camera_networks)
|
||||
var/obj/machinery/camera/C = new(loc, length(visible_networks) > 0)
|
||||
loc = C
|
||||
C.assembly = src
|
||||
|
||||
C.auto_turn()
|
||||
|
||||
C.network = uniquelist(tempnetwork)
|
||||
tempnetwork = difflist(C.network,GLOB.restricted_camera_networks)
|
||||
if(!tempnetwork.len) // Camera isn't on any open network - remove its chunk from AI visibility.
|
||||
GLOB.cameranet.removeCamera(C)
|
||||
C.network = network_list
|
||||
|
||||
C.c_tag = input
|
||||
|
||||
|
||||
@@ -37,9 +37,8 @@
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/ert/proc/register_camera(mob/wearer)
|
||||
if(camera || !has_camera)
|
||||
return
|
||||
camera = new /obj/machinery/camera/portable(src)
|
||||
camera = new /obj/machinery/camera/portable(src, FALSE)
|
||||
camera.network = list("ERT")
|
||||
GLOB.cameranet.removeCamera(camera)
|
||||
camera.c_tag = wearer.name
|
||||
to_chat(wearer, "<span class='notice'>User scanned as [camera.c_tag]. Camera activated.</span>")
|
||||
|
||||
|
||||
@@ -1222,7 +1222,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
var/list/obj/machinery/camera/remove = list()
|
||||
var/list/obj/machinery/camera/visible = list()
|
||||
for(var/datum/camerachunk/CC in eyeobj.visibleCameraChunks)
|
||||
for(var/obj/machinery/camera/C in CC.cameras)
|
||||
for(var/obj/machinery/camera/C in CC.active_cameras)
|
||||
if(!C.can_use() || get_dist(C, eyeobj) > 7)
|
||||
continue
|
||||
visible |= C
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// CAMERA NET
|
||||
//
|
||||
// The datum containing all the chunks.
|
||||
|
||||
#define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc..
|
||||
|
||||
GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new())
|
||||
|
||||
/datum/cameranet
|
||||
@@ -20,16 +17,16 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new())
|
||||
|
||||
// Checks if a chunk has been Generated in x, y, z.
|
||||
/datum/cameranet/proc/chunkGenerated(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
x &= ~(CAMERA_CHUNK_SIZE - 1)
|
||||
y &= ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/key = "[x],[y],[z]"
|
||||
return (chunks[key])
|
||||
|
||||
// Returns the chunk in the x, y, z.
|
||||
// If there is no chunk, it creates a new chunk and returns that.
|
||||
/datum/cameranet/proc/getCameraChunk(x, y, z)
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
x &= ~(CAMERA_CHUNK_SIZE - 1)
|
||||
y &= ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/key = "[x],[y],[z]"
|
||||
if(!chunks[key])
|
||||
chunks[key] = new /datum/camerachunk(null, x, y, z)
|
||||
@@ -55,15 +52,15 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new())
|
||||
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/x1 = max(0, eye.x - static_range) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, eye.y - static_range) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, eye.x + static_range) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, eye.y + static_range) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
|
||||
var/list/visibleChunks = list()
|
||||
|
||||
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
||||
for(var/x = x1; x <= x2; x += CAMERA_CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CAMERA_CHUNK_SIZE)
|
||||
visibleChunks |= getCameraChunk(x, y, eye.z)
|
||||
|
||||
var/list/remove = eye.visibleCameraChunks - visibleChunks
|
||||
@@ -115,22 +112,17 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new())
|
||||
// Removes a camera from a chunk.
|
||||
|
||||
/datum/cameranet/proc/removeCamera(obj/machinery/camera/c)
|
||||
if(c.can_use())
|
||||
majorChunkChange(c, 0)
|
||||
majorChunkChange(c, 0)
|
||||
|
||||
// Add a camera to a chunk.
|
||||
|
||||
/datum/cameranet/proc/addCamera(obj/machinery/camera/c)
|
||||
if(c.can_use())
|
||||
majorChunkChange(c, 1)
|
||||
majorChunkChange(c, 1)
|
||||
|
||||
// Used for Cyborg cameras. Since portable cameras can be in ANY chunk.
|
||||
|
||||
/datum/cameranet/proc/updatePortableCamera(obj/machinery/camera/c)
|
||||
if(c.can_use())
|
||||
majorChunkChange(c, 1)
|
||||
//else
|
||||
// majorChunkChange(c, 0)
|
||||
/datum/cameranet/proc/updatePortableCamera(obj/machinery/camera/c, turf/old_loc)
|
||||
majorChunkChange(c, 1, old_loc)
|
||||
|
||||
// Never access this proc directly!!!!
|
||||
// This will update the chunk and all the surrounding chunks.
|
||||
@@ -138,31 +130,37 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new())
|
||||
// Setting the choice to 0 will remove the camera from the chunks.
|
||||
// If you want to update the chunks around an object, without adding/removing a camera, use choice 2.
|
||||
|
||||
/datum/cameranet/proc/majorChunkChange(atom/c, choice)
|
||||
/datum/cameranet/proc/majorChunkChange(atom/c, choice, turf/old_loc = null)
|
||||
// 0xf = 15
|
||||
if(!c)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(c)
|
||||
if(T)
|
||||
var/x1 = max(0, T.x - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, T.y - (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, T.x + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, T.y + (CHUNK_SIZE / 2)) & ~(CHUNK_SIZE - 1)
|
||||
if(!T)
|
||||
return
|
||||
|
||||
// to_chat(world, "X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]")
|
||||
if(old_loc)
|
||||
// Check if the current turf falls in the same chunka as the old_loc. If so, don't do anything
|
||||
if(T.x & ~(CAMERA_CHUNK_SIZE - 1) == old_loc.x & ~(CAMERA_CHUNK_SIZE - 1) && T.y & ~(CAMERA_CHUNK_SIZE - 1) == old_loc.y & ~(CAMERA_CHUNK_SIZE - 1))
|
||||
return
|
||||
|
||||
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
|
||||
if(chunkGenerated(x, y, T.z))
|
||||
var/datum/camerachunk/chunk = getCameraChunk(x, y, T.z)
|
||||
if(choice == 0)
|
||||
// Remove the camera.
|
||||
chunk.cameras -= c
|
||||
else if(choice == 1)
|
||||
// You can't have the same camera in the list twice.
|
||||
chunk.cameras |= c
|
||||
chunk.hasChanged()
|
||||
// Use camera view distance here to actually know how far a camera can max watch
|
||||
var/x1 = max(0, T.x - CAMERA_VIEW_DISTANCE) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/y1 = max(0, T.y - CAMERA_VIEW_DISTANCE) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/x2 = min(world.maxx, T.x + CAMERA_VIEW_DISTANCE) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
var/y2 = min(world.maxy, T.y + CAMERA_VIEW_DISTANCE) & ~(CAMERA_CHUNK_SIZE - 1)
|
||||
|
||||
for(var/x = x1; x <= x2; x += CAMERA_CHUNK_SIZE)
|
||||
for(var/y = y1; y <= y2; y += CAMERA_CHUNK_SIZE)
|
||||
if(chunkGenerated(x, y, T.z))
|
||||
var/datum/camerachunk/chunk = getCameraChunk(x, y, T.z)
|
||||
if(choice == 0)
|
||||
// Remove the camera.
|
||||
chunk.remove_camera(c)
|
||||
else if(choice == 1)
|
||||
// You can't have the same camera in the list twice.
|
||||
chunk.add_camera(c)
|
||||
chunk.hasChanged()
|
||||
|
||||
// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0.
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#define UPDATE_BUFFER 25 // 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.
|
||||
@@ -9,31 +7,68 @@
|
||||
var/list/obscuredTurfs = list()
|
||||
var/list/visibleTurfs = list()
|
||||
var/list/obscured = list()
|
||||
var/list/cameras = list()
|
||||
var/list/active_cameras = list()
|
||||
var/list/inactive_cameras = list()
|
||||
var/list/turfs = list()
|
||||
var/list/seenby = list()
|
||||
var/visible = FALSE
|
||||
var/changed = FALSE
|
||||
var/updating = FALSE
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/z = 0
|
||||
|
||||
// Add an AI eye to the chunk, then update if changed.
|
||||
/datum/camerachunk/proc/add_camera(obj/machinery/camera/cam)
|
||||
if(active_cameras[cam] || inactive_cameras[cam])
|
||||
return
|
||||
// Register all even though it is active/inactive. Won't get called incorrectly
|
||||
RegisterSignal(cam, COMSIG_CAMERA_OFF, PROC_REF(deactivate_camera))
|
||||
RegisterSignal(cam, COMSIG_CAMERA_ON, PROC_REF(activate_camera))
|
||||
RegisterSignal(cam, COMSIG_PARENT_QDELETING, PROC_REF(remove_camera))
|
||||
RegisterSignal(cam, COMSIG_CAMERA_MOVED, PROC_REF(camera_moved))
|
||||
if(cam.can_use())
|
||||
active_cameras[cam] = cam
|
||||
else
|
||||
inactive_cameras[cam] = cam
|
||||
|
||||
/datum/camerachunk/proc/camera_moved(obj/machinery/camera/cam, atom/old_loc)
|
||||
var/turf/T = get_turf(cam)
|
||||
|
||||
// Falls outside of the chunk view distance
|
||||
if(T.x + CAMERA_VIEW_DISTANCE < x || T.x - CAMERA_VIEW_DISTANCE >= x + CAMERA_CHUNK_SIZE || T.y + CAMERA_VIEW_DISTANCE < y || T.y - CAMERA_VIEW_DISTANCE >= y + CAMERA_CHUNK_SIZE || T.z != z)
|
||||
remove_camera(cam)
|
||||
|
||||
/datum/camerachunk/proc/remove_camera(obj/machinery/camera/cam)
|
||||
UnregisterSignal(cam, list(COMSIG_CAMERA_OFF, COMSIG_CAMERA_ON, COMSIG_PARENT_QDELETING, COMSIG_CAMERA_MOVED))
|
||||
active_cameras -= cam
|
||||
inactive_cameras -= cam
|
||||
SScamera.queue(src)
|
||||
|
||||
/datum/camerachunk/proc/activate_camera(obj/machinery/camera/cam)
|
||||
inactive_cameras -= cam
|
||||
active_cameras += cam
|
||||
SScamera.queue(src)
|
||||
|
||||
/datum/camerachunk/proc/deactivate_camera(obj/machinery/camera/cam)
|
||||
inactive_cameras += cam
|
||||
active_cameras -= cam
|
||||
SScamera.queue(src)
|
||||
|
||||
// Add an AI eye to the chunk, then update if changed.
|
||||
/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
|
||||
RegisterSignal(eye, COMSIG_PARENT_QDELETING, PROC_REF(aiEye_destroyed))
|
||||
if(changed)
|
||||
update()
|
||||
SScamera.queue(src)
|
||||
|
||||
/datum/camerachunk/proc/aiEye_destroyed(mob/camera/aiEye/eye)
|
||||
remove(eye, FALSE)
|
||||
|
||||
// Remove an AI eye from the chunk, then update if changed.
|
||||
|
||||
/datum/camerachunk/proc/remove(mob/camera/aiEye/eye, remove_images = TRUE)
|
||||
if(remove_images)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
@@ -41,8 +76,7 @@
|
||||
client.images -= obscured
|
||||
eye.visibleCameraChunks -= src
|
||||
seenby -= eye
|
||||
if(visible > 0)
|
||||
visible--
|
||||
UnregisterSignal(eye, COMSIG_PARENT_QDELETING)
|
||||
|
||||
// Called when a chunk has changed. I.E: A wall was deleted.
|
||||
|
||||
@@ -55,12 +89,12 @@
|
||||
// instead be flagged to update the next time an AI Eye moves near it.
|
||||
|
||||
/datum/camerachunk/proc/hasChanged(update_now = 0)
|
||||
if(visible || update_now)
|
||||
if(!updating)
|
||||
updating = TRUE
|
||||
spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once
|
||||
update()
|
||||
updating = FALSE
|
||||
if(update_now)
|
||||
update()
|
||||
SScamera.remove_from_queue(src)
|
||||
|
||||
if(length(seenby))
|
||||
SScamera.queue(src)
|
||||
else
|
||||
changed = TRUE
|
||||
|
||||
@@ -69,106 +103,69 @@
|
||||
/datum/camerachunk/proc/update()
|
||||
var/list/newVisibleTurfs = list()
|
||||
|
||||
for(var/camera in cameras)
|
||||
var/obj/machinery/camera/c = camera
|
||||
|
||||
if(!c)
|
||||
continue
|
||||
|
||||
if(!c.can_use())
|
||||
continue
|
||||
|
||||
var/turf/point = locate(src.x + (CHUNK_SIZE / 2), src.y + (CHUNK_SIZE / 2), src.z)
|
||||
if(get_dist(point, c) > CHUNK_SIZE + (CHUNK_SIZE / 2))
|
||||
continue
|
||||
for(var/obj/machinery/camera/c as anything in active_cameras)
|
||||
var/turf/point = locate(src.x + (CAMERA_CHUNK_SIZE / 2), src.y + (CAMERA_CHUNK_SIZE / 2), src.z)
|
||||
var/turf/T = get_turf(c)
|
||||
if(get_dist(point, T) > CAMERA_VIEW_DISTANCE + (CAMERA_CHUNK_SIZE / 2))
|
||||
continue // Still needed for Ais who get created on Z level 1 on the spot of the new player
|
||||
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
newVisibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
newVisibleTurfs &= turfs
|
||||
if(turfs[t])
|
||||
newVisibleTurfs[t] = t
|
||||
|
||||
var/list/visAdded = newVisibleTurfs - visibleTurfs
|
||||
var/list/visRemoved = visibleTurfs - newVisibleTurfs
|
||||
|
||||
visibleTurfs = newVisibleTurfs
|
||||
obscuredTurfs = turfs - newVisibleTurfs
|
||||
|
||||
for(var/turf in visAdded)
|
||||
var/turf/t = turf
|
||||
var/list/images_to_remove = list()
|
||||
var/list/images_to_add = list()
|
||||
for(var/turf/t as anything in visAdded)
|
||||
if(t.obscured)
|
||||
obscured -= t.obscured
|
||||
for(var/eye in seenby)
|
||||
var/mob/camera/aiEye/m = eye
|
||||
if(!m)
|
||||
continue
|
||||
var/client/client = m.GetViewerClient()
|
||||
if(client)
|
||||
client.images -= t.obscured
|
||||
images_to_remove += t.obscured
|
||||
|
||||
for(var/turf in visRemoved)
|
||||
var/turf/t = turf
|
||||
if(obscuredTurfs[t])
|
||||
if(!t.obscured)
|
||||
t.obscured = image('icons/effects/cameravis.dmi', t, null, BYOND_LIGHTING_LAYER + 0.1)
|
||||
t.obscured.plane = BYOND_LIGHTING_PLANE + 1
|
||||
for(var/turf/t as anything in visRemoved)
|
||||
if(!t.obscured)
|
||||
t.obscured = image('icons/effects/cameravis.dmi', t, null, BYOND_LIGHTING_LAYER + 0.1)
|
||||
t.obscured.plane = BYOND_LIGHTING_PLANE + 1
|
||||
obscured += t.obscured
|
||||
images_to_add += t.obscured
|
||||
|
||||
obscured += t.obscured
|
||||
for(var/eye in seenby)
|
||||
var/mob/camera/aiEye/m = eye
|
||||
if(!m)
|
||||
seenby -= m
|
||||
continue
|
||||
var/client/client = m.GetViewerClient()
|
||||
if(client)
|
||||
client.images += t.obscured
|
||||
for(var/mob/camera/aiEye/eye as anything in seenby)
|
||||
var/client/client = eye.GetViewerClient()
|
||||
if(client)
|
||||
client.images -= images_to_remove
|
||||
client.images += images_to_add
|
||||
changed = FALSE
|
||||
|
||||
// Create a new camera chunk, since the chunks are made as they are needed.
|
||||
|
||||
/datum/camerachunk/New(loc, x, y, z)
|
||||
|
||||
// 0xf = 15
|
||||
x &= ~(CHUNK_SIZE - 1)
|
||||
y &= ~(CHUNK_SIZE - 1)
|
||||
x &= ~(CAMERA_CHUNK_SIZE - 1)
|
||||
y &= ~(CAMERA_CHUNK_SIZE - 1)
|
||||
|
||||
src.x = x
|
||||
src.y = y
|
||||
src.z = z
|
||||
|
||||
for(var/obj/machinery/camera/c in urange(CHUNK_SIZE, locate(x + (CHUNK_SIZE / 2), y + (CHUNK_SIZE / 2), z)))
|
||||
if(c.can_use())
|
||||
cameras += c
|
||||
var/half_chunk = CAMERA_CHUNK_SIZE / 2
|
||||
for(var/obj/machinery/camera/c in urange(half_chunk + CAMERA_VIEW_DISTANCE, locate(x + half_chunk, y + half_chunk, z)))
|
||||
add_camera(c)
|
||||
|
||||
for(var/turf/t in block(locate(max(x, 1), max(y, 1), max(z, 1)), locate(min(x + CHUNK_SIZE - 1, world.maxx), min(y + CHUNK_SIZE - 1, world.maxy), z)))
|
||||
for(var/turf/t in block(locate(max(x, 1), max(y, 1), max(z, 1)), locate(min(x + CAMERA_CHUNK_SIZE - 1, world.maxx), min(y + CAMERA_CHUNK_SIZE - 1, world.maxy), z)))
|
||||
turfs[t] = t
|
||||
|
||||
for(var/camera in cameras)
|
||||
var/obj/machinery/camera/c = camera
|
||||
if(!c)
|
||||
continue
|
||||
|
||||
if(!c.can_use())
|
||||
continue
|
||||
|
||||
for(var/obj/machinery/camera/c as anything in active_cameras)
|
||||
for(var/turf/t in c.can_see())
|
||||
// Possible optimization: if(turfs[t]) here, rather than &= turfs afterwards.
|
||||
// List associations use a tree or hashmap of some sort (alongside the list itself)
|
||||
// so are surprisingly fast. (significantly faster than var/thingy/x in list, in testing)
|
||||
visibleTurfs[t] = t
|
||||
|
||||
// Removes turf that isn't in turfs.
|
||||
visibleTurfs &= turfs
|
||||
if(turfs[t])
|
||||
visibleTurfs[t] = t
|
||||
|
||||
obscuredTurfs = turfs - visibleTurfs
|
||||
|
||||
for(var/turf in obscuredTurfs)
|
||||
var/turf/t = turf
|
||||
for(var/turf/t as anything in obscuredTurfs)
|
||||
if(!t.obscured)
|
||||
t.obscured = image('icons/effects/cameravis.dmi', t, "black", BYOND_LIGHTING_LAYER + 0.1)
|
||||
t.obscured.plane = BYOND_LIGHTING_PLANE + 1
|
||||
obscured += t.obscured
|
||||
|
||||
#undef UPDATE_BUFFER
|
||||
|
||||
@@ -99,7 +99,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power.
|
||||
var/lamp_recharging = FALSE //Flag for if the lamp is on cooldown after being forcibly disabled.
|
||||
|
||||
var/updating = FALSE //portable camera camerachunk update
|
||||
/// When the camera moved signal was send last. Avoid overdoing it
|
||||
var/last_camera_update
|
||||
|
||||
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD)
|
||||
|
||||
@@ -1225,19 +1226,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
cell = null
|
||||
qdel(src)
|
||||
|
||||
#define BORG_CAMERA_BUFFER 3 SECONDS
|
||||
#define CAMERA_UPDATE_COOLDOWN 2.5 SECONDS
|
||||
|
||||
/mob/living/silicon/robot/Move(atom/newloc, direct, movetime)
|
||||
var/oldLoc = loc
|
||||
/mob/living/silicon/robot/Moved(atom/OldLoc, Dir, Forced)
|
||||
. = ..()
|
||||
if(. && !updating && camera)
|
||||
updating = TRUE
|
||||
spawn(BORG_CAMERA_BUFFER)
|
||||
if(camera && oldLoc != loc)
|
||||
GLOB.cameranet.updatePortableCamera(camera)
|
||||
updating = FALSE
|
||||
if(camera && last_camera_update + CAMERA_UPDATE_COOLDOWN < world.time)
|
||||
last_camera_update = world.time
|
||||
GLOB.cameranet.updatePortableCamera(camera, OldLoc)
|
||||
SEND_SIGNAL(camera, COMSIG_CAMERA_MOVED, OldLoc)
|
||||
|
||||
#undef BORG_CAMERA_BUFFER
|
||||
#undef CAMERA_UPDATE_COOLDOWN
|
||||
|
||||
/mob/living/silicon/robot/proc/self_destruct()
|
||||
if(emagged)
|
||||
|
||||
Reference in New Issue
Block a user