mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-30 20:22:32 +00:00
* port /tg/'s spatial grid. * fix lint * fix identical variables --------- Co-authored-by: warriorstar-orion <orion@snowfrost.garden>
100 lines
4.7 KiB
Plaintext
100 lines
4.7 KiB
Plaintext
/**
|
|
* Spatial gridmap, cell tracking
|
|
*
|
|
* This datum exists to make the large, repeated "everything in some range" pattern faster
|
|
* Rather then just refreshing against everything, we track all the cells in range of the passed in "window"
|
|
* This lets us do entered/left logic, and make ordinarially quite expensive logic much cheaper
|
|
*
|
|
* Note: This system should not be used for things who have strict requirements about what is NOT in their processed entries
|
|
* It should instead only be used for logic that only really cares about limiting how much gets "entered" in any one call
|
|
* Because we apply this limitation, we can do things to make our code much less prone to unneeded work
|
|
*/
|
|
/datum/cell_tracker
|
|
var/list/datum/spatial_grid_cell/member_cells = list()
|
|
// Inner window
|
|
// If a cell is inside this space, it will be entered into our membership list
|
|
/// The height (y radius) of our inner window
|
|
var/inner_window_x_radius
|
|
/// The width (x radius) of our inner window
|
|
var/inner_window_y_radius
|
|
|
|
// Outer window
|
|
// If a cell is outside this space, it will be removed from our memebership list
|
|
// This effectively applies a grace window, to prevent moving back and forth across a border line causing issues
|
|
/// The height (y radius) of our outer window
|
|
var/outer_window_x_radius
|
|
/// The width (x radius) of our outer window
|
|
var/outer_window_y_radius
|
|
|
|
/// Accepts a width and height to use for this tracker
|
|
/// Also accepts the ratio to use between inner and outer window. Optional, defaults to 2
|
|
/datum/cell_tracker/New(width, height, inner_outer_ratio)
|
|
set_bounds(width, height, inner_outer_ratio)
|
|
return ..()
|
|
|
|
/datum/cell_tracker/Destroy(force)
|
|
stack_trace("Attempted to delete a cell tracker. They don't hold any refs outside of cells, what are you doing")
|
|
if(!force)
|
|
return QDEL_HINT_LETMELIVE
|
|
member_cells.Cut()
|
|
return ..()
|
|
|
|
/// Takes a width and height, and uses them to set the inner window, and interpolate the outer window
|
|
/datum/cell_tracker/proc/set_bounds(width = 0, height = 0, ratio = 2)
|
|
// We want to store these as radii, rather then width and height, since that's convineient for spatial grid code
|
|
var/x_radius = CEILING(width, 2)
|
|
var/y_radius = CEILING(height, 2)
|
|
inner_window_x_radius = x_radius
|
|
inner_window_y_radius = y_radius
|
|
|
|
outer_window_x_radius = x_radius * ratio
|
|
outer_window_y_radius = y_radius * ratio
|
|
|
|
/// Returns a list of newly and formerly joined spatial grid managed objects of type [type] in the form list(new, old)
|
|
/// Takes the center of our window as input
|
|
/datum/cell_tracker/proc/recalculate_type_members(turf/center, type)
|
|
var/list/new_and_old = recalculate_cells(center)
|
|
|
|
var/list/new_members = list()
|
|
var/list/former_members = list()
|
|
/// Pull out all the new and old memebers we want
|
|
switch(type)
|
|
if(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS)
|
|
for(var/datum/spatial_grid_cell/cell as anything in new_and_old[1])
|
|
new_members += cell.client_contents
|
|
for(var/datum/spatial_grid_cell/cell as anything in new_and_old[2])
|
|
former_members += cell.client_contents
|
|
if(SPATIAL_GRID_CONTENTS_TYPE_HEARING)
|
|
for(var/datum/spatial_grid_cell/cell as anything in new_and_old[1])
|
|
new_members += cell.hearing_contents
|
|
for(var/datum/spatial_grid_cell/cell as anything in new_and_old[2])
|
|
former_members += cell.hearing_contents
|
|
if(SPATIAL_GRID_CONTENTS_TYPE_ATMOS)
|
|
for(var/datum/spatial_grid_cell/cell as anything in new_and_old[1])
|
|
new_members += cell.atmos_contents
|
|
for(var/datum/spatial_grid_cell/cell as anything in new_and_old[2])
|
|
former_members += cell.atmos_contents
|
|
|
|
return list(new_members, former_members)
|
|
|
|
/// Recalculates our member list, returns a list in the form list(new members, old members) for reaction
|
|
/// Accepts the turf to use as our "center"
|
|
/datum/cell_tracker/proc/recalculate_cells(turf/center)
|
|
if(!center)
|
|
CRASH("/datum/cell_tracker had an invalid location on refresh, ya done fucked")
|
|
// This is a mild waste of cpu time. Consider optimizing by adding a new helper function to get just the space between two bounds
|
|
// Assuming it ever becomes a real problem
|
|
var/list/datum/spatial_grid_cell/inner_window = SSspatial_grid.get_cells_in_bounds(center, inner_window_x_radius, inner_window_y_radius)
|
|
var/list/datum/spatial_grid_cell/outer_window = SSspatial_grid.get_cells_in_bounds(center, outer_window_x_radius, outer_window_y_radius)
|
|
|
|
var/list/datum/spatial_grid_cell/new_cells = inner_window - member_cells
|
|
// The outer window may contain cells we don't actually have, so we do it like this
|
|
var/list/datum/spatial_grid_cell/old_cells = member_cells - outer_window
|
|
|
|
// This whole thing is a naive implementation,
|
|
// if it turns out to be expensive because of all the list operations I'll look closer at it
|
|
member_cells -= old_cells
|
|
member_cells += new_cells
|
|
|
|
return list(new_cells, old_cells)
|